package command import ( "context" "testing" "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) func TestCommandSide_SetOneTimePassword(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore userPasswordAlg crypto.HashAlgorithm } type args struct { ctx context.Context userID string resourceOwner string password string oneTime bool } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "change password onetime, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), ), expectFilter( eventFromEventPusher( org.NewPasswordComplexityPolicyAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, 1, false, false, false, false, ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, true, "", ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", oneTime: true, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "org1", }, }, }, { name: "change password no one time, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), ), expectFilter( eventFromEventPusher( org.NewPasswordComplexityPolicyAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, 1, false, false, false, false, ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "", ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", oneTime: false, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "org1", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, userPasswordAlg: tt.fields.userPasswordAlg, } got, err := r.SetPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.oneTime) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.want, got) } }) } } func TestCommandSide_SetPassword(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore userPasswordAlg crypto.HashAlgorithm } type args struct { ctx context.Context userID string code string resourceOwner string password string agentID string secretGenerator crypto.Generator } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "password missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "code not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), ), ), }, args: args{ ctx: context.Background(), userID: "user1", code: "aa", resourceOwner: "org1", password: "string", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "invalid code, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanPasswordCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("a"), }, time.Hour*1, domain.NotificationTypeEmail, ), ), ), ), }, args: args{ ctx: context.Background(), userID: "user1", code: "test", resourceOwner: "org1", password: "password", secretGenerator: GetMockSecretGenerator(t), }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "set password, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusherWithCreationDateNow( user.NewHumanPasswordCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("a"), }, time.Hour*1, domain.NotificationTypeEmail, ), ), ), expectFilter( eventFromEventPusher( org.NewPasswordComplexityPolicyAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, 1, false, false, false, false, ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "", ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", code: "a", secretGenerator: GetMockSecretGenerator(t), }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "org1", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, userPasswordAlg: tt.fields.userPasswordAlg, } err := r.SetPasswordWithVerifyCode(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.code, tt.args.password, tt.args.agentID, tt.args.secretGenerator) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } }) } } func TestCommandSide_ChangePassword(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore userPasswordAlg crypto.HashAlgorithm } type args struct { ctx context.Context userID string resourceOwner string oldPassword string newPassword string agentID string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), oldPassword: "password", newPassword: "password1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "old password missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), userID: "user1", newPassword: "password1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "new password missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), userID: "user1", oldPassword: "password", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", oldPassword: "password", newPassword: "password1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "existing password empty, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", oldPassword: "password", newPassword: "password1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "password not matching, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "")), ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", oldPassword: "password-old", newPassword: "password1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "change password, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "")), ), expectFilter( eventFromEventPusher( org.NewPasswordComplexityPolicyAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, 1, false, false, false, false, ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password1"), }, false, "", ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", oldPassword: "password", newPassword: "password1", }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "org1", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, userPasswordAlg: tt.fields.userPasswordAlg, } got, err := r.ChangePassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.oldPassword, tt.args.newPassword, tt.args.agentID) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.want, got) } }) } } func TestCommandSide_RequestSetPassword(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore } type args struct { ctx context.Context userID string resourceOwner string notifyType domain.NotificationType secretGenerator crypto.Generator } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "user initial, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, ), ), eventFromEventPusher( user.NewHumanPhoneChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "+411234567", ), ), eventFromEventPusher( user.NewHumanPhoneVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), ), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "new code, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanInitializedCheckSucceededEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate)), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("a"), }, time.Hour*1, domain.NotificationTypeEmail, ), ), }, ), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", secretGenerator: GetMockSecretGenerator(t), }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "org1", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } if tt.res.err == nil { assert.Equal(t, tt.res.want, got) } }) } } func TestCommandSide_PasswordCodeSent(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore } type args struct { ctx context.Context userID string resourceOwner string } type res struct { err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "code sent, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanPhoneChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "+411234567", ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordCodeSentEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), }, ), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } err := r.PasswordCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } }) } } func TestCommandSide_CheckPassword(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore userPasswordAlg crypto.HashAlgorithm } type args struct { ctx context.Context userID string resourceOwner string password string authReq *domain.AuthRequest lockoutPolicy *domain.LockoutPolicy } type res struct { err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "userid missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), password: "password", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "password missing, invalid argument error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "login policy not found, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "login policy login password not allowed, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, false, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "user not existing, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, true, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), expectFilter(), ), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "existing password empty, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, true, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", password: "password", resourceOwner: "org1", }, res: res{ err: caos_errs.IsPreconditionFailed, }, }, { name: "password not matching lockout policy not relevant, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, true, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "")), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordCheckFailedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &user.AuthRequestInfo{ ID: "request1", UserAgentID: "agent1", }, ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", password: "password1", resourceOwner: "org1", authReq: &domain.AuthRequest{ ID: "request1", AgentID: "agent1", }, lockoutPolicy: &domain.LockoutPolicy{}, }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "password not matching, max password attempts reached - user locked, precondition error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, true, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "")), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordCheckFailedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &user.AuthRequestInfo{ ID: "request1", UserAgentID: "agent1", }, ), ), eventFromEventPusher( user.NewUserLockedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", password: "password1", resourceOwner: "org1", authReq: &domain.AuthRequest{ ID: "request1", AgentID: "agent1", }, lockoutPolicy: &domain.LockoutPolicy{ MaxPasswordAttempts: 1, }, }, res: res{ err: caos_errs.IsErrorInvalidArgument, }, }, { name: "check password, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, true, false, false, false, false, false, domain.PasswordlessTypeNotAllowed, "", time.Hour*1, time.Hour*2, time.Hour*3, time.Hour*4, time.Hour*5, ), ), ), expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", "firstname", "lastname", "nickname", "displayname", language.German, domain.GenderUnspecified, "email@test.ch", true, ), ), eventFromEventPusher( user.NewHumanEmailVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, ), ), eventFromEventPusher( user.NewHumanPasswordChangedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &crypto.CryptoValue{ CryptoType: crypto.TypeHash, Algorithm: "hash", KeyID: "", Crypted: []byte("password"), }, false, "")), ), expectPush( []*repository.Event{ eventFromEventPusher( user.NewHumanPasswordCheckSucceededEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, &user.AuthRequestInfo{ ID: "request1", UserAgentID: "agent1", }, ), ), }, ), ), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), userID: "user1", resourceOwner: "org1", password: "password", authReq: &domain.AuthRequest{ ID: "request1", AgentID: "agent1", }, }, res: res{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, userPasswordAlg: tt.fields.userPasswordAlg, } err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy) if tt.res.err == nil { assert.NoError(t, err) } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) } }) } }