authelia/internal/handlers/handler_reset_password_step1.go
Manuel Nuñez 8659ba394d
feat(authentication): password policy (#2723)
Implement a password policy with visual feedback in the web portal.

Co-authored-by: Manuel Nuñez <@mind-ar>
Co-authored-by: James Elliott <james-d-elliott@users.noreply.github.com>
2022-04-03 08:32:57 +10:00

83 lines
2.9 KiB
Go

package handlers
import (
"encoding/json"
"fmt"
"time"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/session"
)
func identityRetrieverFromStorage(ctx *middlewares.AutheliaCtx) (*session.Identity, error) {
var requestBody resetPasswordStep1RequestBody
err := json.Unmarshal(ctx.PostBody(), &requestBody)
if err != nil {
return nil, err
}
details, err := ctx.Providers.UserProvider.GetDetails(requestBody.Username)
if err != nil {
return nil, err
}
if len(details.Emails) == 0 {
return nil, fmt.Errorf("user %s has no email address configured", requestBody.Username)
}
return &session.Identity{
Username: requestBody.Username,
Email: details.Emails[0],
}, nil
}
// ResetPasswordIdentityStart the handler for initiating the identity validation for resetting a password.
// We need to ensure the attacker cannot perform user enumeration by always replying with 200 whatever what happens in backend.
var ResetPasswordIdentityStart = middlewares.IdentityVerificationStart(middlewares.IdentityVerificationStartArgs{
MailTitle: "Reset your password",
MailButtonContent: "Reset",
TargetEndpoint: "/reset-password/step2",
ActionClaim: ActionResetPassword,
IdentityRetrieverFunc: identityRetrieverFromStorage,
}, middlewares.TimingAttackDelay(10, 250, 85, time.Millisecond*500))
func resetPasswordIdentityFinish(ctx *middlewares.AutheliaCtx, username string) {
userSession := ctx.GetSession()
// TODO(c.michaud): use JWT tokens to expire the request in only few seconds for better security.
userSession.PasswordResetUsername = &username
err := ctx.SaveSession(userSession)
if err != nil {
ctx.Logger.Errorf("Unable to clear password reset flag in session for user %s: %s", userSession.Username, err)
}
mode := ""
if ctx.Configuration.PasswordPolicy.Standard.Enabled {
mode = "standard"
} else if ctx.Configuration.PasswordPolicy.Zxcvbn.Enabled {
mode = "zxcvbn"
}
policyResponse := PassworPolicyBody{
Mode: mode,
MinLength: ctx.Configuration.PasswordPolicy.Standard.MinLength,
MaxLength: ctx.Configuration.PasswordPolicy.Standard.MaxLength,
RequireLowercase: ctx.Configuration.PasswordPolicy.Standard.RequireLowercase,
RequireUppercase: ctx.Configuration.PasswordPolicy.Standard.RequireUppercase,
RequireNumber: ctx.Configuration.PasswordPolicy.Standard.RequireNumber,
RequireSpecial: ctx.Configuration.PasswordPolicy.Standard.RequireSpecial,
MinScore: ctx.Configuration.PasswordPolicy.Zxcvbn.MinScore,
}
err = ctx.SetJSONBody(policyResponse)
if err != nil {
ctx.Logger.Errorf("Unable to send password Policy: %s", err)
}
}
// ResetPasswordIdentityFinish the handler for finishing the identity validation.
var ResetPasswordIdentityFinish = middlewares.IdentityVerificationFinish(
middlewares.IdentityVerificationFinishArgs{ActionClaim: ActionResetPassword}, resetPasswordIdentityFinish)