fix: check password complexity policy and respect changeRequired on password change (#7884)

* fix: check password complexity policy on password change and respect require_change

* pass changeRequired where available and add tests

* fix requested changes

---------

Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
Livio Spring
2024-05-02 11:50:13 +02:00
committed by GitHub
parent 8cc12e869a
commit 43da9225be
11 changed files with 509 additions and 199 deletions

View File

@@ -43,10 +43,10 @@ type Profile struct {
type Password struct {
// Either you have to have permission, a password code or the old password to change
PasswordCode *string
OldPassword *string
Password *string
EncodedPasswordHash *string
PasswordCode string
OldPassword string
Password string
EncodedPasswordHash string
ChangeRequired bool
}
@@ -73,12 +73,12 @@ func (h *ChangeHuman) Validate(hasher *crypto.Hasher) (err error) {
}
func (p *Password) Validate(hasher *crypto.Hasher) error {
if p.EncodedPasswordHash != nil {
if !hasher.EncodingSupported(*p.EncodedPasswordHash) {
if p.EncodedPasswordHash != "" {
if !hasher.EncodingSupported(p.EncodedPasswordHash) {
return zerrors.ThrowInvalidArgument(nil, "USER-oz74onzvqr", "Errors.User.Password.NotSupported")
}
}
if p.Password == nil && p.EncodedPasswordHash == nil {
if p.Password == "" && p.EncodedPasswordHash == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-3klek4sbns", "Errors.User.Password.Empty")
}
return nil
@@ -285,7 +285,7 @@ func (c *Commands) ChangeUserHuman(ctx context.Context, human *ChangeHuman, alg
}
}
if human.Password != nil {
cmds, err = c.changeUserPassword(ctx, cmds, existingHuman, human.Password, alg)
cmds, err = c.changeUserPassword(ctx, cmds, existingHuman, human.Password)
if err != nil {
return err
}
@@ -370,57 +370,34 @@ func changeUserProfile(ctx context.Context, cmds []eventstore.Command, wm *UserV
return cmds, err
}
func (c *Commands) changeUserPassword(ctx context.Context, cmds []eventstore.Command, wm *UserV2WriteModel, password *Password, alg crypto.EncryptionAlgorithm) ([]eventstore.Command, error) {
func (c *Commands) changeUserPassword(ctx context.Context, cmds []eventstore.Command, wm *UserV2WriteModel, password *Password) ([]eventstore.Command, error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.End() }()
// Either have a code to set the password
if password.PasswordCode != nil {
if err := crypto.VerifyCode(wm.PasswordCodeCreationDate, wm.PasswordCodeExpiry, wm.PasswordCode, *password.PasswordCode, alg); err != nil {
return cmds, err
}
// if no verification is set, the user must have the permission to change the password
verification := c.setPasswordWithPermission(wm.AggregateID, wm.ResourceOwner)
// otherwise check the password code...
if password.PasswordCode != "" {
verification = c.setPasswordWithVerifyCode(wm.PasswordCodeCreationDate, wm.PasswordCodeExpiry, wm.PasswordCode, password.PasswordCode)
}
var encodedPassword string
// or have the old password to change it
if password.OldPassword != nil {
// newly encode old password if no new and already encoded password is set
pw := *password.OldPassword
if password.Password != nil {
pw = *password.Password
}
alreadyEncodedPassword, err := c.verifyAndUpdatePassword(ctx, wm.PasswordEncodedHash, *password.OldPassword, pw)
if err != nil {
return cmds, err
}
encodedPassword = alreadyEncodedPassword
// ...or old password
if password.OldPassword != "" {
verification = c.checkCurrentPassword(password.Password, password.EncodedPasswordHash, password.OldPassword, wm.PasswordEncodedHash)
}
// password already hashed in request
if password.EncodedPasswordHash != nil {
cmd, err := c.setPasswordCommand(ctx, &wm.Aggregate().Aggregate, wm.UserState, *password.EncodedPasswordHash, "", password.ChangeRequired, true)
if cmd != nil {
return append(cmds, cmd), err
}
return cmds, err
cmd, err := c.setPasswordCommand(
ctx,
&wm.Aggregate().Aggregate,
wm.UserState,
password.Password,
password.EncodedPasswordHash,
"",
password.ChangeRequired,
verification,
)
if cmd != nil {
return append(cmds, cmd), err
}
// password already hashed in verify
if encodedPassword != "" {
cmd, err := c.setPasswordCommand(ctx, &wm.Aggregate().Aggregate, wm.UserState, encodedPassword, "", password.ChangeRequired, true)
if cmd != nil {
return append(cmds, cmd), err
}
return cmds, err
}
// password still to be hashed
if password.Password != nil {
cmd, err := c.setPasswordCommand(ctx, &wm.Aggregate().Aggregate, wm.UserState, *password.Password, "", password.ChangeRequired, false)
if cmd != nil {
return append(cmds, cmd), err
}
return cmds, err
}
// no password changes necessary
return cmds, nil
return cmds, err
}
func (c *Commands) userExistsWriteModel(ctx context.Context, userID string) (writeModel *UserV2WriteModel, err error) {