Skip to content

Commit

Permalink
Merge pull request #20 from isd-sgcu/feature/JOH-52
Browse files Browse the repository at this point in the history
[JOH-52] Implement reset password rpc
  • Loading branch information
Nitiwat-owen authored Jan 15, 2024
2 parents 9e9c31c + c30c36c commit 5eda981
Show file tree
Hide file tree
Showing 4 changed files with 355 additions and 2 deletions.
1 change: 1 addition & 0 deletions internal/constant/error.constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package constant

const InvalidTokenErrorMessage = "Invalid token"
const IncorrectEmailPasswordErrorMessage = "Incorrect email or password"
const IncorrectPasswordErrorMessage = "New password should not be the same as the previous one"
const DuplicateEmailErrorMessage = "Duplicate email"
const InternalServerErrorMessage = "Internal server error"

Expand Down
36 changes: 35 additions & 1 deletion internal/service/auth/auth.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,39 @@ func (s *serviceImpl) ForgotPassword(_ context.Context, request *authProto.Forgo
}

func (s *serviceImpl) ResetPassword(_ context.Context, request *authProto.ResetPasswordRequest) (*authProto.ResetPasswordResponse, error) {
return nil, nil
resetTokenCache, err := s.tokenService.FindResetPasswordToken(request.Token)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

userDb := &model.User{}
if err := s.userRepo.FindById(resetTokenCache.UserID, userDb); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, constant.UserNotFoundErrorMessage)
}
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

err = s.bcryptUtil.CompareHashedPassword(userDb.Password, request.Password)
if err == nil {
return nil, status.Error(codes.InvalidArgument, constant.IncorrectPasswordErrorMessage)
}

hashPassword, err := s.bcryptUtil.GenerateHashedPassword(request.Password)
if err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

userDb.Password = hashPassword
if err := s.userRepo.Update(resetTokenCache.UserID, userDb); err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

if err := s.tokenService.RemoveResetPasswordToken(request.Token); err != nil {
return nil, status.Error(codes.Internal, constant.InternalServerErrorMessage)
}

return &authProto.ResetPasswordResponse{
IsSuccess: true,
}, nil
}
311 changes: 311 additions & 0 deletions internal/service/auth/auth.service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type AuthServiceTest struct {
refreshTokenRequest *authProto.RefreshTokenRequest
validateRequest *authProto.ValidateRequest
forgotPasswordRequest *authProto.ForgotPasswordRequest
resetPasswordRequest *authProto.ResetPasswordRequest
authConfig cfgldr.Auth
}

Expand Down Expand Up @@ -67,6 +68,10 @@ func (t *AuthServiceTest) SetupTest() {
forgotPasswordRequest := &authProto.ForgotPasswordRequest{
Email: faker.Email(),
}
resetPasswordRequest := &authProto.ResetPasswordRequest{
Token: faker.Word(),
Password: faker.Password(),
}
authConfig := cfgldr.Auth{
ClientURL: "localhost",
}
Expand All @@ -78,6 +83,7 @@ func (t *AuthServiceTest) SetupTest() {
t.validateRequest = validateRequest
t.refreshTokenRequest = refreshTokenRequest
t.forgotPasswordRequest = forgotPasswordRequest
t.resetPasswordRequest = resetPasswordRequest
t.authConfig = authConfig
}

Expand Down Expand Up @@ -897,3 +903,308 @@ func (t *AuthServiceTest) TestForgotPasswordSendEmailFailed() {
assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordSuccess() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
userDb := &model.User{
Base: model.Base{
ID: uuid.New(),
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.FirstName(),
Lastname: faker.LastName(),
Role: constant.USER,
}
comparePasswordErr := errors.New("Internal error")
hashedPassword := faker.Word()
updateUserDb := &model.User{
Base: model.Base{
ID: userDb.ID,
},
Email: userDb.Email,
Password: hashedPassword,
Firstname: userDb.Firstname,
Lastname: userDb.Lastname,
Role: userDb.Role,
}

expected := &authProto.ResetPasswordResponse{
IsSuccess: true,
}

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(userDb, nil)
bcryptUtil.On("CompareHashedPassword", userDb.Password, t.resetPasswordRequest.Password).Return(comparePasswordErr)
bcryptUtil.On("GenerateHashedPassword", t.resetPasswordRequest.Password).Return(hashedPassword, nil)
userRepo.On("Update", resetTokenCache.UserID, updateUserDb).Return(updateUserDb, nil)
tokenService.On("RemoveResetPasswordToken", t.resetPasswordRequest.Token).Return(nil)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), err)
assert.Equal(t.T(), expected, actual)
}

func (t *AuthServiceTest) TestResetPasswordFindTokenFailed() {
findTokenErr := errors.New("Internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(nil, findTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordFindUserNotFound() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
findUserErr := gorm.ErrRecordNotFound

expected := status.Error(codes.NotFound, constant.UserNotFoundErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(nil, findUserErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordFindUserInternalError() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
findUserErr := errors.New("Internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(nil, findUserErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordSamePassword() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
userDb := &model.User{
Base: model.Base{
ID: uuid.New(),
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.FirstName(),
Lastname: faker.LastName(),
Role: constant.USER,
}

expected := status.Error(codes.InvalidArgument, constant.IncorrectPasswordErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(userDb, nil)
bcryptUtil.On("CompareHashedPassword", userDb.Password, t.resetPasswordRequest.Password).Return(nil)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordGenerateHashedPasswordFailed() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
comparePasswordErr := errors.New("Internal error")
userDb := &model.User{
Base: model.Base{
ID: uuid.New(),
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.FirstName(),
Lastname: faker.LastName(),
Role: constant.USER,
}
generatePasswordErr := errors.New("Internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(userDb, nil)
bcryptUtil.On("CompareHashedPassword", userDb.Password, t.resetPasswordRequest.Password).Return(comparePasswordErr)
bcryptUtil.On("GenerateHashedPassword", t.resetPasswordRequest.Password).Return("", generatePasswordErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordUpdateUserFailed() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
userDb := &model.User{
Base: model.Base{
ID: uuid.New(),
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.FirstName(),
Lastname: faker.LastName(),
Role: constant.USER,
}
comparePasswordErr := errors.New("Internal error")
hashedPassword := faker.Word()
updateUserDb := &model.User{
Base: model.Base{
ID: userDb.ID,
},
Email: userDb.Email,
Password: hashedPassword,
Firstname: userDb.Firstname,
Lastname: userDb.Lastname,
Role: userDb.Role,
}
updateUserErr := errors.New("Internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(userDb, nil)
bcryptUtil.On("CompareHashedPassword", userDb.Password, t.resetPasswordRequest.Password).Return(comparePasswordErr)
bcryptUtil.On("GenerateHashedPassword", t.resetPasswordRequest.Password).Return(hashedPassword, nil)
userRepo.On("Update", resetTokenCache.UserID, updateUserDb).Return(nil, updateUserErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}

func (t *AuthServiceTest) TestResetPasswordRemoveTokenFailed() {
resetTokenCache := &tokenDto.ResetPasswordTokenCache{
UserID: faker.UUIDDigit(),
}
userDb := &model.User{
Base: model.Base{
ID: uuid.New(),
},
Email: faker.Email(),
Password: faker.Password(),
Firstname: faker.FirstName(),
Lastname: faker.LastName(),
Role: constant.USER,
}
comparePasswordErr := errors.New("Internal error")
hashedPassword := faker.Word()
updateUserDb := &model.User{
Base: model.Base{
ID: userDb.ID,
},
Email: userDb.Email,
Password: hashedPassword,
Firstname: userDb.Firstname,
Lastname: userDb.Lastname,
Role: userDb.Role,
}
removeTokenErr := errors.New("Internal error")

expected := status.Error(codes.Internal, constant.InternalServerErrorMessage)

controller := gomock.NewController(t.T())

authRepo := mock_auth.NewMockRepository(controller)
userRepo := user.UserRepositoryMock{}
tokenService := token.TokenServiceMock{}
emailService := email.EmailServiceMock{}
bcryptUtil := utils.BcryptUtilMock{}

tokenService.On("FindResetPasswordToken", t.resetPasswordRequest.Token).Return(resetTokenCache, nil)
userRepo.On("FindById", resetTokenCache.UserID, &model.User{}).Return(userDb, nil)
bcryptUtil.On("CompareHashedPassword", userDb.Password, t.resetPasswordRequest.Password).Return(comparePasswordErr)
bcryptUtil.On("GenerateHashedPassword", t.resetPasswordRequest.Password).Return(hashedPassword, nil)
userRepo.On("Update", resetTokenCache.UserID, updateUserDb).Return(updateUserDb, nil)
tokenService.On("RemoveResetPasswordToken", t.resetPasswordRequest.Token).Return(removeTokenErr)

authSvc := NewService(authRepo, &userRepo, &tokenService, &emailService, &bcryptUtil, t.authConfig)
actual, err := authSvc.ResetPassword(t.ctx, t.resetPasswordRequest)

assert.Nil(t.T(), actual)
assert.Equal(t.T(), expected, err)
}
Loading

0 comments on commit 5eda981

Please sign in to comment.