diff --git a/mocks/user_service.go b/mocks/user_service.go index 74d2d38..21768a2 100644 --- a/mocks/user_service.go +++ b/mocks/user_service.go @@ -28,6 +28,9 @@ func (m *UserServiceMock) GetUserByKey(s string, r bool) (*models.User, error) { func (m *UserServiceMock) GetUserByEmail(s string) (*models.User, error) { args := m.Called(s) + if args.Get(0) == nil { + return nil, args.Error(1) + } return args.Get(0).(*models.User), args.Error(1) } diff --git a/routes/login.go b/routes/login.go index a37f2b2..593d328 100644 --- a/routes/login.go +++ b/routes/login.go @@ -106,9 +106,12 @@ func (h *LoginHandler) PostLogin(w http.ResponseWriter, r *http.Request) { user, err := h.userSrvc.GetUserById(login.Username) if err != nil { - w.WriteHeader(http.StatusNotFound) - templates[conf.LoginTemplate].Execute(w, h.buildViewModel(r, w, false).WithError("resource not found")) - return + // try to get by email if given username is an email address (checked inside service) + if user, err = h.userSrvc.GetUserByEmail(login.Username); err != nil { + w.WriteHeader(http.StatusNotFound) + templates[conf.LoginTemplate].Execute(w, h.buildViewModel(r, w, false).WithError("resource not found")) + return + } } if !utils.ComparePassword(user.Password, login.Password, h.config.Security.PasswordSalt) { diff --git a/routes/login_test.go b/routes/login_test.go index f71cef3..81e259c 100644 --- a/routes/login_test.go +++ b/routes/login_test.go @@ -162,6 +162,7 @@ func (suite *LoginHandlerTestSuite) TestPostLogin_NonExistingUser() { w := httptest.NewRecorder() suite.UserService.On("GetUserById", "nonexisting").Return(nil, errors.New("")) + suite.UserService.On("GetUserByEmail", "nonexisting").Return(nil, errors.New("")) suite.UserService.On("Count").Return(1, nil) suite.Sut.PostLogin(w, r) diff --git a/services/user.go b/services/user.go index e6ec397..6c68f4c 100644 --- a/services/user.go +++ b/services/user.go @@ -10,6 +10,7 @@ import ( "github.com/duke-git/lancet/v2/convertor" "github.com/duke-git/lancet/v2/datetime" + "github.com/duke-git/lancet/v2/validator" "github.com/gofrs/uuid/v5" "github.com/leandro-lugaresi/hub" "github.com/patrickmn/go-cache" @@ -127,6 +128,9 @@ func (srv *UserService) GetUserByEmail(email string) (*models.User, error) { if email == "" { return nil, errors.New("email must not be empty") } + if !validator.IsEmail(email) { + return nil, errors.New("not a valid email") + } return srv.repository.FindOne(models.User{Email: email}) } diff --git a/services/user_test.go b/services/user_test.go index db0a4df..0dbf3b7 100644 --- a/services/user_test.go +++ b/services/user_test.go @@ -39,6 +39,38 @@ func TestUserServiceTestSuite(t *testing.T) { suite.Run(t, new(UserServiceTestSuite)) } +func (suite *UserServiceTestSuite) TestUserService_GetByEmail_Empty() { + sut := NewUserService(suite.KeyValueService, suite.MailService, suite.ApiKeyService, suite.UserRepo) + + result, err := sut.GetUserByEmail("") + + suite.Nil(result) + suite.NotNil(err) + suite.Equal(err, errors.New("email must not be empty")) +} + +func (suite *UserServiceTestSuite) TestUserService_GetByEmail_Invalid() { + sut := NewUserService(suite.KeyValueService, suite.MailService, suite.ApiKeyService, suite.UserRepo) + + result, err := sut.GetUserByEmail("notanemailaddress") + + suite.Nil(result) + suite.NotNil(err) + suite.Equal(err, errors.New("not a valid email")) +} + +func (suite *UserServiceTestSuite) TestUserService_GetByEmail_Valid() { + const testEmail = "foo@bar.com" + + suite.UserRepo.On("FindOne", models.User{Email: testEmail}).Return(suite.TestUser, nil) + + sut := NewUserService(suite.KeyValueService, suite.MailService, suite.ApiKeyService, suite.UserRepo) + result, err := sut.GetUserByEmail(testEmail) + + suite.Equal(suite.TestUser, result) + suite.Nil(err) +} + func (suite *UserServiceTestSuite) TestUserService_GetByEmptyKey_Failed() { sut := NewUserService(suite.KeyValueService, suite.MailService, suite.ApiKeyService, suite.UserRepo) diff --git a/testing/wakapi_api_tests/Auth/Login (by email).bru b/testing/wakapi_api_tests/Auth/Login (by email).bru new file mode 100644 index 0000000..1d8b5ad --- /dev/null +++ b/testing/wakapi_api_tests/Auth/Login (by email).bru @@ -0,0 +1,32 @@ +meta { + name: Login (by email) + type: http + seq: 5 +} + +post { + url: {{BASE_URL}}/login + body: formUrlEncoded + auth: none +} + +body:form-urlencoded { + username: testuser@wakapi.dev + password: testpassword +} + +assert { + res.status: eq 302 + res.headers['location']: eq /summary +} + +script:pre-request { + // Do not follow 3xx redirects + req.setMaxRedirects(0) +} + +tests { + test("Sets cookie", function () { + expect(res.headers["set-cookie"].some(str => str.includes("wakapi_auth="))).to.be.true + }); +}