feat: PasswordResetBegin of auth controller;
fix: sql query updateLoginInformationByUsername used in-database hashing; refactor: renamed LogOutAccounts into LogOutSessions in models/auth; refactor: added error checks on opening transactions for all auth service methods; refactor: added error checks on commiting transactions likewise; refactor: simplified PasswordResetBegin logic; feat: implemented PasswordResetComplete method of auth service;
This commit is contained in:
@@ -65,7 +65,14 @@ func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequ
|
||||
var passwordHash string
|
||||
var err error
|
||||
|
||||
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
|
||||
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to open a transaction",
|
||||
zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
defer helper.RollbackOnError(err)
|
||||
|
||||
// TODO: check occupation with redis
|
||||
@@ -184,7 +191,12 @@ func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequ
|
||||
zap.String("code", generatedCode))
|
||||
}
|
||||
|
||||
helper.Commit()
|
||||
if err = helper.Commit(); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to commit transaction",
|
||||
zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -198,7 +210,13 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
var accessToken, refreshToken string
|
||||
var err error
|
||||
|
||||
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
|
||||
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to open a transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
defer helper.RollbackOnError(err)
|
||||
|
||||
user, err = db.TXQueries.GetUserByUsername(db.CTX, request.Username)
|
||||
@@ -316,7 +334,12 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
helper.Commit()
|
||||
if err = helper.Commit(); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to commit transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
a.log.Info(
|
||||
"User verified registration",
|
||||
@@ -336,7 +359,13 @@ func (a *authServiceImpl) Login(request models.LoginRequest) (*models.LoginRespo
|
||||
var session database.Session
|
||||
var err error
|
||||
|
||||
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
|
||||
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to open a transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
defer helper.RollbackOnError(err)
|
||||
|
||||
userRow, err = db.TXQueries.GetValidUserByLoginCredentials(db.CTX, database.GetValidUserByLoginCredentialsParams{
|
||||
@@ -395,7 +424,12 @@ func (a *authServiceImpl) Login(request models.LoginRequest) (*models.LoginRespo
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
helper.Commit()
|
||||
if err = helper.Commit(); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to commit transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
response := models.LoginResponse{Tokens: models.Tokens{
|
||||
AccessToken: accessToken,
|
||||
@@ -416,6 +450,12 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
|
||||
var err error
|
||||
|
||||
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to open a transaction",
|
||||
zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
defer helper.RollbackOnError(err)
|
||||
|
||||
ctx := context.TODO()
|
||||
@@ -427,15 +467,13 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
|
||||
zap.String("email", request.Email),
|
||||
zap.Error(redisErr))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
} else if err == nil {
|
||||
current_time := time.Now()
|
||||
if current_time.Unix() < cooldownTimeUnix {
|
||||
a.log.Warn(
|
||||
"Attempted to request a new password reset code for email on active reset cooldown",
|
||||
zap.String("email", request.Email))
|
||||
return false, errs.ErrTooManyRequests
|
||||
}
|
||||
if time.Now().Unix() < cooldownTimeUnix {
|
||||
a.log.Warn(
|
||||
"Attempted to request a new password reset code for email on active reset cooldown",
|
||||
zap.String("email", request.Email))
|
||||
return false, errs.ErrTooManyRequests
|
||||
}
|
||||
|
||||
if user, err = db.TXQueries.GetUserByEmail(db.CTX, request.Email); err != nil {
|
||||
@@ -501,13 +539,144 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
|
||||
return false, err
|
||||
}
|
||||
|
||||
helper.Commit()
|
||||
if err = helper.Commit(); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to commit transaction",
|
||||
zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *authServiceImpl) PasswordResetComplete(request models.PasswordResetCompleteRequest) (*models.PasswordResetCompleteResponse, error) {
|
||||
|
||||
return nil, errs.ErrNotImplemented
|
||||
var resetCode database.ConfirmationCode
|
||||
var user database.User
|
||||
var session database.Session
|
||||
var hashedPassword, accessToken, refreshToken string
|
||||
var err error
|
||||
|
||||
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
|
||||
if err != nil {
|
||||
a.log.Error(
|
||||
"Failed to open a transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
if user, err = db.TXQueries.GetUserByEmail(db.CTX, request.Email); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
a.log.Warn(
|
||||
"Attempted to complete password reset for unregistered email",
|
||||
zap.String("email", request.Email),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrForbidden
|
||||
}
|
||||
|
||||
a.log.Error(
|
||||
"Failed to look up user of email while trying to complete password reset",
|
||||
zap.String("email", request.Email),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
if resetCode, err = db.TXQueries.GetValidConfirmationCodeByCode(db.CTX, database.GetValidConfirmationCodeByCodeParams{
|
||||
UserID: user.ID,
|
||||
CodeType: int32(enums.PasswordResetCodeType),
|
||||
Code: request.VerificationCode,
|
||||
}); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
a.log.Warn(
|
||||
"Attempted to reset password for user using incorrect confirmation code",
|
||||
zap.String("email", request.Email),
|
||||
zap.String("username", user.Username),
|
||||
zap.String("provided_code", request.VerificationCode),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrForbidden
|
||||
}
|
||||
}
|
||||
|
||||
if err = db.TXQueries.UpdateConfirmationCode(db.CTX, database.UpdateConfirmationCodeParams{
|
||||
ID: resetCode.ID,
|
||||
Used: utils.NewPointer(true),
|
||||
}); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to invalidate password reset code upon use",
|
||||
zap.String("username", user.Username),
|
||||
zap.String("email", request.Email),
|
||||
zap.Int64("code_id", resetCode.ID),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
if hashedPassword, err = utils.HashPassword(request.NewPassword); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to hash new password as part of user password reset",
|
||||
zap.String("email", request.Email),
|
||||
zap.String("username", user.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
if err = db.TXQueries.UpdateLoginInformationByUsername(db.CTX, database.UpdateLoginInformationByUsernameParams{
|
||||
Username: user.Username,
|
||||
PasswordHash: hashedPassword,
|
||||
}); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to save new password to database as part of user password reset",
|
||||
zap.String("username", user.Username),
|
||||
zap.String("email", request.Email),
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
if request.LogOutSessions {
|
||||
if err = db.TXQueries.TerminateAllSessionsForUserByUsername(db.CTX, user.Username); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to log out older sessions as part of user password reset",
|
||||
zap.String("email", request.Email),
|
||||
zap.String("username", user.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
}
|
||||
|
||||
if session, err = db.TXQueries.CreateSession(db.CTX, database.CreateSessionParams{
|
||||
UserID: user.ID,
|
||||
Name: utils.NewPointer("First device"),
|
||||
Platform: utils.NewPointer("Unknown"),
|
||||
LatestIp: utils.NewPointer("Unknown"),
|
||||
}); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to create new session for user as part of user password reset",
|
||||
zap.String("email", request.Email),
|
||||
zap.String("username", user.Username),
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
if accessToken, refreshToken, err = utils.GenerateTokens(user.Username, session.Guid.String()); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to generate tokens as part of user password reset",
|
||||
zap.String("email", request.Email),
|
||||
zap.String("username", user.Username),
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
response := models.PasswordResetCompleteResponse{
|
||||
Tokens: models.Tokens{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
},
|
||||
}
|
||||
|
||||
if err = helper.Commit(); err != nil {
|
||||
a.log.Error(
|
||||
"Failed to commit transaction",
|
||||
zap.Error(err))
|
||||
return nil, errs.ErrServerError
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user