From aa4432dc99a445a12725ab063e4522f020be3a9c Mon Sep 17 00:00:00 2001 From: "Marco A." Date: Tue, 17 Mar 2026 11:50:47 +0100 Subject: [PATCH] feat(session): IDP Intent Check API with relation tables (#11820) # Which Problems Are Solved As part of #11035 , this PR implements the IDP intent check logic for session validation # How the Problems Are Solved - Inject encryption algorithm for IDP intent tokens at DB initialization - Add the IDP intent repository as an invoke option - Rework crypto mocks using gomock - Implement IDP intent check logic - minor - DB: expand IDP Intent deletion testing table with a test case where IDP intent is not found # Noteworthy I did not implement `domain.Transactional` given the doubts that @adlerhurst expressed in https://github.com/zitadel/zitadel/pull/11804 . So, there is no transaction AT ALL in here because the `Execute()` method contains one single DB call (Delete) and `Validate()` just does Get calls. # Additional Context This is a cherry-picked PR + minor changes, coming from https://github.com/zitadel/zitadel/pull/11164 - Relates to #11035 - Depends on https://github.com/zitadel/zitadel/pull/11804 --- .../v2/convert/session_check_idp_intent.go | 17 + .../convert/session_check_idp_intent_test.go | 55 ++ backend/v3/domain/defaults.go | 15 +- backend/v3/domain/errors.go | 23 + backend/v3/domain/invoke_option.go | 7 + backend/v3/domain/mock/idp_intent_repo.go | 43 + backend/v3/domain/session_check_idp_intent.go | 161 ++++ .../domain/session_check_idp_intent_test.go | 821 ++++++++++++++++++ .../database/repository/idp_intent_test.go | 5 + cmd/start/start.go | 1 + internal/crypto/code_mock.go | 1 + internal/crypto/code_mocker.go | 29 +- internal/crypto/crypto.go | 2 + internal/crypto/crypto.mock.go | 271 ++++++ internal/crypto/crypto_mock.go | 126 --- .../handlers/user_notifier_test.go | 2 +- 16 files changed, 1422 insertions(+), 157 deletions(-) create mode 100644 backend/v3/api/session/v2/convert/session_check_idp_intent.go create mode 100644 backend/v3/api/session/v2/convert/session_check_idp_intent_test.go create mode 100644 backend/v3/domain/mock/idp_intent_repo.go create mode 100644 backend/v3/domain/session_check_idp_intent.go create mode 100644 backend/v3/domain/session_check_idp_intent_test.go create mode 100644 internal/crypto/crypto.mock.go delete mode 100644 internal/crypto/crypto_mock.go diff --git a/backend/v3/api/session/v2/convert/session_check_idp_intent.go b/backend/v3/api/session/v2/convert/session_check_idp_intent.go new file mode 100644 index 000000000..db82571a6 --- /dev/null +++ b/backend/v3/api/session/v2/convert/session_check_idp_intent.go @@ -0,0 +1,17 @@ +package convert + +import ( + "github.com/zitadel/zitadel/backend/v3/domain" + session_grpc "github.com/zitadel/zitadel/pkg/grpc/session/v2" +) + +func CheckIDPIntentGRPCToDomain(checkIDPIntent *session_grpc.CheckIDPIntent) *domain.CheckIDPIntentType { + if checkIDPIntent == nil { + return nil + } + + return &domain.CheckIDPIntentType{ + ID: checkIDPIntent.GetIdpIntentId(), + Token: checkIDPIntent.GetIdpIntentToken(), + } +} diff --git a/backend/v3/api/session/v2/convert/session_check_idp_intent_test.go b/backend/v3/api/session/v2/convert/session_check_idp_intent_test.go new file mode 100644 index 000000000..1ca56268f --- /dev/null +++ b/backend/v3/api/session/v2/convert/session_check_idp_intent_test.go @@ -0,0 +1,55 @@ +package convert + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/backend/v3/domain" + session_grpc "github.com/zitadel/zitadel/pkg/grpc/session/v2" +) + +func TestCheckIDPIntentGRPCToDomain(t *testing.T) { + t.Parallel() + tt := []struct { + testName string + input *session_grpc.CheckIDPIntent + expected *domain.CheckIDPIntentType + }{ + { + testName: "nil input", + input: nil, + expected: nil, + }, + { + testName: "valid input", + input: &session_grpc.CheckIDPIntent{ + IdpIntentId: "intent-123", + IdpIntentToken: "token-abc", + }, + expected: &domain.CheckIDPIntentType{ + ID: "intent-123", + Token: "token-abc", + }, + }, + { + testName: "empty strings", + input: &session_grpc.CheckIDPIntent{ + IdpIntentId: "", + IdpIntentToken: "", + }, + expected: &domain.CheckIDPIntentType{ + ID: "", + Token: "", + }, + }, + } + + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + result := CheckIDPIntentGRPCToDomain(tc.input) + assert.Equal(t, tc.expected, result) + }) + } +} diff --git a/backend/v3/domain/defaults.go b/backend/v3/domain/defaults.go index e08177425..0d69c9616 100644 --- a/backend/v3/domain/defaults.go +++ b/backend/v3/domain/defaults.go @@ -8,10 +8,13 @@ import ( ) var ( - pool database.Pool - legacyEventstore eventstore.LegacyEventstore - sysConfig systemdefaults.SystemDefaults - passwordHasher *crypto.Hasher + pool database.Pool + // tracer tracing.Tracer = tracing.GlobalTracer() + // runtimeLogger *slog.Logger = logging.New(logging.StreamRuntime, nil) + legacyEventstore eventstore.LegacyEventstore + sysConfig systemdefaults.SystemDefaults + passwordHasher *crypto.Hasher + idpEncryptionAlgo crypto.EncryptionAlgorithm ) func SetPool(p database.Pool) { @@ -29,3 +32,7 @@ func SetSystemConfig(cfg systemdefaults.SystemDefaults) { func SetPasswordHasher(hasher *crypto.Hasher) { passwordHasher = hasher } + +func SetIDPEncryptionAlgorithm(idpEncryptionAlg crypto.EncryptionAlgorithm) { + idpEncryptionAlgo = idpEncryptionAlg +} diff --git a/backend/v3/domain/errors.go b/backend/v3/domain/errors.go index a07303408..6191422d8 100644 --- a/backend/v3/domain/errors.go +++ b/backend/v3/domain/errors.go @@ -1,7 +1,11 @@ package domain import ( + "errors" "fmt" + + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/internal/zerrors" ) type wrongIDPTypeError struct { @@ -89,6 +93,25 @@ func (e *PasswordVerificationError) Error() string { return fmt.Sprintf("Message=failed password attempts (%d)", e.failedAttempts) } +// handleGetError wraps DB errors coming from Get calls into [zerrors] errors. +// +// - errorID should be in the format - +// - objectType should be the string representation of a DB object (e.g. 'user', 'session', 'idp'...) +// +// The function wraps [database.NoRowFoundError] to [zerrors.NotFound] error +// and any other error to [zerrors.InternalError] +func handleGetError(inputErr error, errorID, objectType string) error { + if inputErr == nil { + return nil + } + + if errors.Is(inputErr, &database.NoRowFoundError{}) { + return zerrors.CreateZitadelError(zerrors.KindNotFound, inputErr, errorID, fmt.Sprintf("%s not found", objectType), 1) + } + + return zerrors.CreateZitadelError(zerrors.KindInternal, inputErr, errorID, fmt.Sprintf("failed fetching %s", objectType), 1) +} + func (err *PasswordVerificationError) Is(target error) bool { _, ok := target.(*PasswordVerificationError) return ok diff --git a/backend/v3/domain/invoke_option.go b/backend/v3/domain/invoke_option.go index 4d307e61b..1c2f43040 100644 --- a/backend/v3/domain/invoke_option.go +++ b/backend/v3/domain/invoke_option.go @@ -63,6 +63,12 @@ func WithPermissionChecker(checker PermissionChecker) InvokeOpt { } } +func WithIDPIntentRepo(repo IDPIntentRepository) InvokeOpt { + return func(opts *InvokeOpts) { + opts.idpIntentRepo = repo + } +} + // WithQueryExecutor sets the database client to be used by the command. // If not set, the default pool will be used. // This is mainly used for testing. @@ -99,6 +105,7 @@ type InvokeOpts struct { instanceDomainRepo InstanceDomainRepository sessionRepo SessionRepository userRepo UserRepository + idpIntentRepo IDPIntentRepository // Settings repos lockoutSettingRepo LockoutSettingsRepository diff --git a/backend/v3/domain/mock/idp_intent_repo.go b/backend/v3/domain/mock/idp_intent_repo.go new file mode 100644 index 000000000..6c83f5c3c --- /dev/null +++ b/backend/v3/domain/mock/idp_intent_repo.go @@ -0,0 +1,43 @@ +package domainmock + +import ( + context "context" + + gomock "go.uber.org/mock/gomock" + + domain "github.com/zitadel/zitadel/backend/v3/domain" + database "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" +) + +func NewIDPIntentRepo(ctrl *gomock.Controller) *IDPIntentRepo { + return &IDPIntentRepo{ + mock: NewMockIDPIntentRepository(ctrl), + IDPIntentRepository: repository.IDPIntentRepository(), + } +} + +type IDPIntentRepo struct { + domain.IDPIntentRepository + mock *MockIDPIntentRepository +} + +func (u *IDPIntentRepo) EXPECT() *MockIDPIntentRepositoryMockRecorder { + return u.mock.EXPECT() +} + +func (i *IDPIntentRepo) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.IDPIntent, error) { + return i.mock.Get(ctx, client, opts...) +} + +func (i *IDPIntentRepo) Create(ctx context.Context, client database.QueryExecutor, intent *domain.IDPIntent) error { + return i.mock.Create(ctx, client, intent) +} + +func (i *IDPIntentRepo) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { + return i.mock.Update(ctx, client, condition, changes...) +} + +func (i *IDPIntentRepo) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) { + return i.mock.Delete(ctx, client, condition) +} diff --git a/backend/v3/domain/session_check_idp_intent.go b/backend/v3/domain/session_check_idp_intent.go new file mode 100644 index 000000000..bac694c15 --- /dev/null +++ b/backend/v3/domain/session_check_idp_intent.go @@ -0,0 +1,161 @@ +package domain + +import ( + "context" + "time" + + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/idpintent" + "github.com/zitadel/zitadel/internal/repository/session" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type CheckIDPIntentType struct { + ID string + Token string +} + +type IDPIntentCheckCommand struct { + CheckIntent *CheckIDPIntentType + + SessionID string + InstanceID string + EncAlgo crypto.EncryptionAlgorithm + + FetchedUser User + IsCheckComplete bool +} + +// NewIDPIntentCheckCommand returns an IDPIntentCheckCommand initialized with the input values. +// +// If encryptionAlgo is nil, the default [crypto.EncryptionAlgorithm] will be used +func NewIDPIntentCheckCommand(request *CheckIDPIntentType, sessionID, instanceID string, encryptionAlgo crypto.EncryptionAlgorithm) *IDPIntentCheckCommand { + idpCheckCommand := &IDPIntentCheckCommand{ + CheckIntent: request, + SessionID: sessionID, + InstanceID: instanceID, + EncAlgo: idpEncryptionAlgo, + } + + if encryptionAlgo != nil { + idpCheckCommand.EncAlgo = encryptionAlgo + } + + return idpCheckCommand +} + +// Events implements [Commander]. +func (i *IDPIntentCheckCommand) Events(ctx context.Context, opts *InvokeOpts) ([]eventstore.Command, error) { + if i.CheckIntent == nil || !i.IsCheckComplete { + return nil, nil + } + + return []eventstore.Command{ + session.NewIntentCheckedEvent(ctx, &session.NewAggregate(i.SessionID, i.InstanceID).Aggregate, time.Now()), + idpintent.NewConsumedEvent(ctx, &idpintent.NewAggregate(i.CheckIntent.ID, "").Aggregate), + }, nil +} + +// Execute implements [Commander]. +func (i *IDPIntentCheckCommand) Execute(ctx context.Context, opts *InvokeOpts) (err error) { + if i.CheckIntent == nil { + return nil + } + + intentRepo := opts.idpIntentRepo + + removedRows, err := intentRepo.Delete(ctx, opts.DB(), intentRepo.PrimaryKeyCondition(i.InstanceID, i.CheckIntent.ID)) + if err != nil { + return zerrors.ThrowInternal(err, "DOM-j1s5Eu", "failed deleting IDP intent") + } + if removedRows != 1 { + return zerrors.ThrowInternal(NewRowsReturnedMismatchError(1, removedRows), "DOM-3CBpdB", "unexpected number of rows deleted") + } + + i.IsCheckComplete = true + + return nil +} + +// String implements [Commander]. +func (i *IDPIntentCheckCommand) String() string { + return "IDPIntentCheckCommand" +} + +// Validate implements [Commander]. +func (i *IDPIntentCheckCommand) Validate(ctx context.Context, opts *InvokeOpts) (err error) { + if i.CheckIntent == nil { + return nil + } + + if i.SessionID == "" { + return zerrors.ThrowPreconditionFailed(nil, "DOM-5Y8pb4", "Errors.Missing.SessionID") + } + if i.InstanceID == "" { + return zerrors.ThrowPreconditionFailed(nil, "DOM-Q4YFIq", "Errors.Missing.InstanceID") + } + + sessionRepo := opts.sessionRepo + idpIntentRepo := opts.idpIntentRepo + userRepo := opts.userRepo + + session, err := sessionRepo.Get(ctx, opts.DB(), database.WithCondition(sessionRepo.PrimaryKeyCondition(i.InstanceID, i.SessionID))) + if err = handleGetError(err, "DOM-EhIgey", "session"); err != nil { + return err + } + + if session.UserID == "" { + return zerrors.ThrowPreconditionFailed(nil, "DOM-IJcVkV", "Errors.User.UserIDMissing") + } + + if err := crypto.CheckToken(i.EncAlgo, i.CheckIntent.Token, i.CheckIntent.ID); err != nil { + return err + } + + intent, err := idpIntentRepo.Get(ctx, opts.DB(), database.WithCondition(idpIntentRepo.PrimaryKeyCondition(i.InstanceID, i.CheckIntent.ID))) + if err = handleGetError(err, "DOM-5XkWJV", "intent"); err != nil { + return err + } + + if intent.State != IDPIntentStateSucceeded { + return zerrors.ThrowPreconditionFailed(nil, "DOM-0UKHku", "Errors.Intent.NotSucceeded") + } + + if intent.ExpiresAt == nil || intent.ExpiresAt.Before(time.Now()) { + return zerrors.ThrowPreconditionFailed(nil, "DOM-kDR1XK", "Errors.Intent.Expired") + } + + user, err := userRepo.Get(ctx, opts.DB(), database.WithCondition(userRepo.PrimaryKeyCondition(i.InstanceID, session.UserID))) + if err = handleGetError(err, "DOM-Vnx2G9", "user"); err != nil { + return err + } + if user.Human == nil { + return zerrors.ThrowInternal(nil, "DOM-FkX5lZ", "user not human") + } + + if intentUsr := intent.UserID; intentUsr != "" { + if intentUsr != session.UserID { + return zerrors.ThrowPreconditionFailed(nil, "DOM-FLdnLH", "Errors.Intent.OtherUser") + } + i.FetchedUser = *user + return nil + } + + var matchingLink *IdentityProviderLink + for _, idpLink := range user.Human.IdentityProviderLinks { + if idpLink.ProviderID == intent.IDPID { + matchingLink = idpLink + break + } + } + if matchingLink == nil { + return zerrors.ThrowPreconditionFailed(nil, "DOM-XuNkt7", "Errors.Intent.OtherUser") + } + + i.FetchedUser = *user + return nil +} + +var _ Commander = (*IDPIntentCheckCommand)(nil) diff --git a/backend/v3/domain/session_check_idp_intent_test.go b/backend/v3/domain/session_check_idp_intent_test.go new file mode 100644 index 000000000..5ab9f4ff2 --- /dev/null +++ b/backend/v3/domain/session_check_idp_intent_test.go @@ -0,0 +1,821 @@ +package domain_test + +import ( + "context" + "encoding/base64" + "errors" + "testing" + "time" + + "github.com/muhlemmer/gu" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/zitadel/zitadel/backend/v3/domain" + domainmock "github.com/zitadel/zitadel/backend/v3/domain/mock" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/dbmock" + noopdb "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/noop" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/idpintent" + "github.com/zitadel/zitadel/internal/repository/session" + "github.com/zitadel/zitadel/internal/zerrors" +) + +func TestIDPIntentCheckCommand_Events(t *testing.T) { + t.Parallel() + tt := []struct { + name string + command *domain.IDPIntentCheckCommand + expectedEvents []eventstore.Command + }{ + { + name: "nil CheckIntent returns nil events", + command: &domain.IDPIntentCheckCommand{ + CheckIntent: nil, + IsCheckComplete: true, + }, + }, + { + name: "isCheckComplete false returns nil events", + command: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{}, + }, + }, + { + name: "valid command returns events", + command: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123"}, + SessionID: "session-456", + InstanceID: "instance-789", + IsCheckComplete: true, + }, + + expectedEvents: []eventstore.Command{ + session.NewIntentCheckedEvent(t.Context(), &session.NewAggregate("session-456", "instance-789").Aggregate, time.Now()), + idpintent.NewConsumedEvent(t.Context(), &idpintent.NewAggregate("intent-123", "").Aggregate), + }, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // Test + events, err := tc.command.Events(context.Background(), &domain.InvokeOpts{}) + + // Verify + assert.NoError(t, err) + + require.Len(t, events, len(tc.expectedEvents)) + + for i, expectedType := range tc.expectedEvents { + assert.IsType(t, expectedType, events[i]) + switch expectedAssertedType := expectedType.(type) { + case *session.IntentCheckedEvent: + actualAssertedType, ok := events[i].(*session.IntentCheckedEvent) + require.True(t, ok) + assert.InDelta(t, expectedAssertedType.CheckedAt.UnixMilli(), actualAssertedType.CheckedAt.UnixMilli(), 10) + case *idpintent.ConsumedEvent: + _, ok := events[i].(*idpintent.ConsumedEvent) + require.True(t, ok) + } + } + }) + } +} + +func TestIDPIntentCheckCommand_Validate(t *testing.T) { + t.Parallel() + getErr := errors.New("get error") + notFoundErr := database.NewNoRowFoundError(nil) + decryptionError := errors.New("decryption error") + invalidTokenEncoding := base64.RawURLEncoding.EncodeToString([]byte("invalid-token")) + validTokenEncoding := base64.RawURLEncoding.EncodeToString([]byte("token-123")) + + tt := []struct { + testName string + sessionRepo func(ctrl *gomock.Controller) domain.SessionRepository + idpIntentRepo func(ctrl *gomock.Controller) domain.IDPIntentRepository + userRepo func(ctrl *gomock.Controller) domain.UserRepository + encryptionAlgorithm func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm + cmd *domain.IDPIntentCheckCommand + expectedError error + expectedUser domain.User + }{ + { + testName: "when CheckIntent is nil should return no error", + cmd: &domain.IDPIntentCheckCommand{}, + expectedError: nil, + }, + { + testName: "when session ID is not set should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "", + InstanceID: "instance-1", + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-5Y8pb4", "Errors.Missing.SessionID"), + }, + { + testName: "when instance ID is not set should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "", + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-Q4YFIq", "Errors.Missing.InstanceID"), + }, + { + testName: "when retrieving session fails should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(nil, getErr) + return repo + }, + expectedError: zerrors.ThrowInternal(getErr, "DOM-EhIgey", "failed fetching session"), + }, + { + testName: "when session not found should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(nil, notFoundErr) + return repo + }, + expectedError: zerrors.ThrowNotFound(notFoundErr, "DOM-EhIgey", "session not found"), + }, + { + testName: "when session userID is empty should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: ""}, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-IJcVkV", "Errors.User.UserIDMissing"), + }, + { + testName: "when token verification fails should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: invalidTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("", decryptionError) + return cryptoAlg + }, + expectedError: zerrors.ThrowPermissionDenied(decryptionError, "CRYPTO-Sf4gt", "Errors.Intent.InvalidToken"), + }, + { + testName: "when retrieving intent fails should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(nil, getErr) + return repo + }, + expectedError: zerrors.ThrowInternal(getErr, "DOM-5XkWJV", "failed fetching intent"), + }, + { + testName: "when intent not found should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(nil, notFoundErr) + return repo + }, + expectedError: zerrors.ThrowNotFound(notFoundErr, "DOM-5XkWJV", "intent not found"), + }, + { + testName: "when intent state is not succeeded should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateFailed, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour))}, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-0UKHku", "Errors.Intent.NotSucceeded"), + }, + { + testName: "when intent is expired should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(-time.Hour))}, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-kDR1XK", "Errors.Intent.Expired"), + }, + { + testName: "when intent expiration is nil should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: nil}, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-kDR1XK", "Errors.Intent.Expired"), + }, + { + testName: "when retrieving user fails should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: ""}, nil) + return repo + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(nil, getErr) + return repo + }, + expectedError: zerrors.ThrowInternal(getErr, "DOM-Vnx2G9", "failed fetching user"), + }, + { + testName: "when user is not found should return not found error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: ""}, nil) + return repo + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(nil, notFoundErr) + return repo + }, + expectedError: zerrors.ThrowNotFound(notFoundErr, "DOM-Vnx2G9", "user not found"), + }, + { + testName: "when user is not human should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: ""}, nil) + return repo + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(&domain.User{ID: "user-1", State: domain.UserStateActive, Human: nil}, nil) + return repo + }, + expectedError: zerrors.ThrowInternal(nil, "DOM-FkX5lZ", "user not human"), + }, + { + testName: "when intent has user and it matches session user should return no error and set fetched user", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(&domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{IdentityProviderLinks: []*domain.IdentityProviderLink{{ProviderID: "idp-123"}}}, + }, nil) + return repo + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: "user-1"}, nil) + return repo + }, + expectedError: nil, + expectedUser: domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + IdentityProviderLinks: []*domain.IdentityProviderLink{ + {ProviderID: "idp-123"}, + }, + }, + }, + }, + { + testName: "when intent has user but it does not match session user should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(&domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{IdentityProviderLinks: []*domain.IdentityProviderLink{{ProviderID: "idp-123"}}}, + }, nil) + return repo + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: "user-2"}, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-FLdnLH", "Errors.Intent.OtherUser"), + }, + { + testName: "when user has no matching IDP link should return error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: "", IDPID: "idp-123"}, nil) + return repo + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(&domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{IdentityProviderLinks: []*domain.IdentityProviderLink{{ProviderID: "idp-456"}}}, + }, nil) + return repo + }, + expectedError: zerrors.ThrowPreconditionFailed(nil, "DOM-XuNkt7", "Errors.Intent.OtherUser"), + }, + { + testName: "when all validations pass should return no error", + cmd: &domain.IDPIntentCheckCommand{ + CheckIntent: &domain.CheckIDPIntentType{ID: "intent-123", Token: validTokenEncoding}, + SessionID: "session-1", + InstanceID: "instance-1", + }, + sessionRepo: func(ctrl *gomock.Controller) domain.SessionRepository { + repo := domainmock.NewSessionRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "session-1")))). + Return(&domain.Session{ID: "session-1", UserID: "user-1"}, nil) + return repo + }, + encryptionAlgorithm: func(ctrl *gomock.Controller) crypto.EncryptionAlgorithm { + cryptoAlg := crypto.NewMockEncryptionAlgorithm(ctrl) + cryptoAlg.EXPECT(). + EncryptionKeyID(). + Times(1) + cryptoAlg.EXPECT(). + DecryptString(gomock.Any(), gomock.Any()). + Times(1). + Return("intent-123", nil) + return cryptoAlg + }, + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "intent-123")))). + Return(&domain.IDPIntent{ID: "intent-123", State: domain.IDPIntentStateSucceeded, ExpiresAt: gu.Ptr(time.Now().Add(time.Hour)), UserID: "", IDPID: "idp-123"}, nil) + return repo + }, + userRepo: func(ctrl *gomock.Controller) domain.UserRepository { + repo := domainmock.NewUserRepo(ctrl) + repo.EXPECT(). + Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(repo.PrimaryKeyCondition("instance-1", "user-1")))). + Return(&domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + IdentityProviderLinks: []*domain.IdentityProviderLink{ + {ProviderID: "idp-123"}, + }, + }, + }, nil) + return repo + }, + expectedError: nil, + expectedUser: domain.User{ + ID: "user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + IdentityProviderLinks: []*domain.IdentityProviderLink{ + {ProviderID: "idp-123"}, + }, + }, + }, + }, + } + + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + // Given + ctrl := gomock.NewController(t) + ctx := authz.NewMockContext(tc.cmd.InstanceID, "", "") + + opts := &domain.InvokeOpts{ + Invoker: domain.NewTransactionInvoker(nil), + } + domain.WithQueryExecutor(new(noopdb.Pool))(opts) + + if tc.sessionRepo != nil { + domain.WithSessionRepo(tc.sessionRepo(ctrl))(opts) + } + if tc.userRepo != nil { + domain.WithUserRepo(tc.userRepo(ctrl))(opts) + } + if tc.idpIntentRepo != nil { + domain.WithIDPIntentRepo(tc.idpIntentRepo(ctrl))(opts) + } + if tc.encryptionAlgorithm != nil { + tc.cmd.EncAlgo = tc.encryptionAlgorithm(ctrl) + } + + // Test + err := tc.cmd.Validate(ctx, opts) + + // Verify + assert.ErrorIs(t, err, tc.expectedError) + assert.Equal(t, tc.expectedUser, tc.cmd.FetchedUser) + }) + } +} + +func TestIDPIntentCheckCommand_Execute(t *testing.T) { + t.Parallel() + deleteErr := errors.New("delete error") + + tt := []struct { + testName string + idpIntentRepo func(ctrl *gomock.Controller) domain.IDPIntentRepository + cmd *domain.IDPIntentCheckCommand + expectedError error + expectComplete bool + }{ + { + testName: "when CheckIntent is nil should return no error and not mark complete", + cmd: &domain.IDPIntentCheckCommand{}, + expectedError: nil, + expectComplete: false, + }, + { + testName: "when intent deletion fails should return error", + cmd: domain.NewIDPIntentCheckCommand(&domain.CheckIDPIntentType{ID: "intent-123"}, "session-1", "instance-1", nil), + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Delete(gomock.Any(), gomock.Any(), repo.PrimaryKeyCondition("instance-1", "intent-123")). + Return(int64(0), deleteErr) + return repo + }, + expectedError: zerrors.ThrowInternal(deleteErr, "DOM-j1s5Eu", "failed deleting IDP intent"), + expectComplete: false, + }, + { + testName: "when intent deletion returns unexpected row count should return error", + cmd: domain.NewIDPIntentCheckCommand(&domain.CheckIDPIntentType{ID: "intent-123"}, "session-1", "instance-1", nil), + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Delete(gomock.Any(), gomock.Any(), repo.PrimaryKeyCondition("instance-1", "intent-123")). + Return(int64(0), nil) + return repo + }, + expectedError: zerrors.ThrowInternal(domain.NewRowsReturnedMismatchError(1, 0), "DOM-3CBpdB", "unexpected number of rows deleted"), + expectComplete: false, + }, + { + testName: "when intent is successfully deleted should mark complete", + cmd: domain.NewIDPIntentCheckCommand(&domain.CheckIDPIntentType{ID: "intent-123"}, "session-1", "instance-1", nil), + idpIntentRepo: func(ctrl *gomock.Controller) domain.IDPIntentRepository { + repo := domainmock.NewIDPIntentRepo(ctrl) + repo.EXPECT(). + Delete(gomock.Any(), gomock.Any(), repo.PrimaryKeyCondition("instance-1", "intent-123")). + Return(int64(1), nil) + return repo + }, + expectedError: nil, + expectComplete: true, + }, + } + + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // t.Parallel() + // Given + ctrl := gomock.NewController(t) + ctx := authz.NewMockContext(tc.cmd.InstanceID, "", "") + + opts := &domain.InvokeOpts{ + Invoker: domain.NewTransactionInvoker(nil), + } + domain.WithQueryExecutor(new(noopdb.Pool))(opts) + + if tc.idpIntentRepo != nil { + domain.WithIDPIntentRepo(tc.idpIntentRepo(ctrl))(opts) + } + + // Test + err := tc.cmd.Execute(ctx, opts) + + // Verify + assert.ErrorIs(t, err, tc.expectedError) + assert.Equal(t, tc.expectComplete, tc.cmd.IsCheckComplete) + }) + } +} diff --git a/backend/v3/storage/database/repository/idp_intent_test.go b/backend/v3/storage/database/repository/idp_intent_test.go index c6e127815..ce1459e72 100644 --- a/backend/v3/storage/database/repository/idp_intent_test.go +++ b/backend/v3/storage/database/repository/idp_intent_test.go @@ -570,6 +570,11 @@ func TestDeleteIDPIntent(t *testing.T) { ), expectedDeletedCount: 2, }, + { + testName: "when condition matches no record should not delete anything", + inputConditions: idpIntentRepo.PrimaryKeyCondition(instanceID, "not found"), + expectedDeletedCount: 0, + }, } for _, tc := range tt { diff --git a/cmd/start/start.go b/cmd/start/start.go index 4e0dbc766..46aa465e0 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -209,6 +209,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server new_domain.SetLegacyEventstore(eventstoreClient) new_domain.SetSystemConfig(config.SystemDefaults) + new_domain.SetIDPEncryptionAlgorithm(keys.IDPConfig) sessionTokenVerifier := internal_authz.SessionTokenVerifier(keys.OIDC) cacheConnectors, err := connector.StartConnectors(config.Caches, dbClient) diff --git a/internal/crypto/code_mock.go b/internal/crypto/code_mock.go index 3ed342c7d..c308681fb 100644 --- a/internal/crypto/code_mock.go +++ b/internal/crypto/code_mock.go @@ -20,6 +20,7 @@ import ( type MockGenerator struct { ctrl *gomock.Controller recorder *MockGeneratorMockRecorder + isgomock struct{} } // MockGeneratorMockRecorder is the mock recorder for MockGenerator. diff --git a/internal/crypto/code_mocker.go b/internal/crypto/code_mocker.go index 59cece2af..eee2c126a 100644 --- a/internal/crypto/code_mocker.go +++ b/internal/crypto/code_mocker.go @@ -9,37 +9,14 @@ import ( ) func CreateMockEncryptionAlg(ctrl *gomock.Controller) EncryptionAlgorithm { - return createMockEncryptionAlgorithm( - ctrl, - func(code []byte) ([]byte, error) { - return code, nil - }, - ) -} - -// CreateMockEncryptionAlgWithCode compares the length of the value to be encrypted with the length of the provided code. -// It will return an error if they do not match. -// The provided code will be used to encrypt in favor of the value passed to the encryption. -// This function is intended to be used where the passed value is not in control, but where the returned encryption requires a static value. -func CreateMockEncryptionAlgWithCode(ctrl *gomock.Controller, code string) EncryptionAlgorithm { - return createMockEncryptionAlgorithm( - ctrl, - func(c []byte) ([]byte, error) { - if len(c) != len(code) { - return nil, zerrors.ThrowInvalidArgumentf(nil, "id", "invalid code length - expected %d, got %d", len(code), len(c)) - } - return []byte(code), nil - }, - ) -} - -func createMockEncryptionAlgorithm(ctrl *gomock.Controller, encryptFunction func(c []byte) ([]byte, error)) *MockEncryptionAlgorithm { mCrypto := NewMockEncryptionAlgorithm(ctrl) mCrypto.EXPECT().Algorithm().AnyTimes().Return("enc") mCrypto.EXPECT().EncryptionKeyID().AnyTimes().Return("id") mCrypto.EXPECT().DecryptionKeyIDs().AnyTimes().Return([]string{"id"}) mCrypto.EXPECT().Encrypt(gomock.Any()).AnyTimes().DoAndReturn( - encryptFunction, + func(code []byte) ([]byte, error) { + return code, nil + }, ) mCrypto.EXPECT().DecryptString(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn( func(code []byte, keyID string) (string, error) { diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index cd84a5f15..5669709e0 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -14,6 +14,8 @@ const ( TypeHash // Depcrecated: use [passwap.Swapper] instead ) +//go:generate mockgen -typed -package crypto -destination ./crypto.mock.go . EncryptionAlgorithm + type EncryptionAlgorithm interface { Algorithm() string EncryptionKeyID() string diff --git a/internal/crypto/crypto.mock.go b/internal/crypto/crypto.mock.go new file mode 100644 index 000000000..b9672e738 --- /dev/null +++ b/internal/crypto/crypto.mock.go @@ -0,0 +1,271 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/zitadel/zitadel/internal/crypto (interfaces: EncryptionAlgorithm) +// +// Generated by this command: +// +// mockgen -typed -package crypto -destination ./crypto.mock.go . EncryptionAlgorithm +// + +// Package crypto is a generated GoMock package. +package crypto + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockEncryptionAlgorithm is a mock of EncryptionAlgorithm interface. +type MockEncryptionAlgorithm struct { + ctrl *gomock.Controller + recorder *MockEncryptionAlgorithmMockRecorder + isgomock struct{} +} + +// MockEncryptionAlgorithmMockRecorder is the mock recorder for MockEncryptionAlgorithm. +type MockEncryptionAlgorithmMockRecorder struct { + mock *MockEncryptionAlgorithm +} + +// NewMockEncryptionAlgorithm creates a new mock instance. +func NewMockEncryptionAlgorithm(ctrl *gomock.Controller) *MockEncryptionAlgorithm { + mock := &MockEncryptionAlgorithm{ctrl: ctrl} + mock.recorder = &MockEncryptionAlgorithmMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEncryptionAlgorithm) EXPECT() *MockEncryptionAlgorithmMockRecorder { + return m.recorder +} + +// Algorithm mocks base method. +func (m *MockEncryptionAlgorithm) Algorithm() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Algorithm") + ret0, _ := ret[0].(string) + return ret0 +} + +// Algorithm indicates an expected call of Algorithm. +func (mr *MockEncryptionAlgorithmMockRecorder) Algorithm() *MockEncryptionAlgorithmAlgorithmCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Algorithm", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Algorithm)) + return &MockEncryptionAlgorithmAlgorithmCall{Call: call} +} + +// MockEncryptionAlgorithmAlgorithmCall wrap *gomock.Call +type MockEncryptionAlgorithmAlgorithmCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmAlgorithmCall) Return(arg0 string) *MockEncryptionAlgorithmAlgorithmCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmAlgorithmCall) Do(f func() string) *MockEncryptionAlgorithmAlgorithmCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmAlgorithmCall) DoAndReturn(f func() string) *MockEncryptionAlgorithmAlgorithmCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Decrypt mocks base method. +func (m *MockEncryptionAlgorithm) Decrypt(hashed []byte, keyID string) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Decrypt", hashed, keyID) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Decrypt indicates an expected call of Decrypt. +func (mr *MockEncryptionAlgorithmMockRecorder) Decrypt(hashed, keyID any) *MockEncryptionAlgorithmDecryptCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decrypt", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Decrypt), hashed, keyID) + return &MockEncryptionAlgorithmDecryptCall{Call: call} +} + +// MockEncryptionAlgorithmDecryptCall wrap *gomock.Call +type MockEncryptionAlgorithmDecryptCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmDecryptCall) Return(arg0 []byte, arg1 error) *MockEncryptionAlgorithmDecryptCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmDecryptCall) Do(f func([]byte, string) ([]byte, error)) *MockEncryptionAlgorithmDecryptCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmDecryptCall) DoAndReturn(f func([]byte, string) ([]byte, error)) *MockEncryptionAlgorithmDecryptCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DecryptString mocks base method. +func (m *MockEncryptionAlgorithm) DecryptString(hashed []byte, keyID string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecryptString", hashed, keyID) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DecryptString indicates an expected call of DecryptString. +func (mr *MockEncryptionAlgorithmMockRecorder) DecryptString(hashed, keyID any) *MockEncryptionAlgorithmDecryptStringCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptString", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).DecryptString), hashed, keyID) + return &MockEncryptionAlgorithmDecryptStringCall{Call: call} +} + +// MockEncryptionAlgorithmDecryptStringCall wrap *gomock.Call +type MockEncryptionAlgorithmDecryptStringCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmDecryptStringCall) Return(arg0 string, arg1 error) *MockEncryptionAlgorithmDecryptStringCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmDecryptStringCall) Do(f func([]byte, string) (string, error)) *MockEncryptionAlgorithmDecryptStringCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmDecryptStringCall) DoAndReturn(f func([]byte, string) (string, error)) *MockEncryptionAlgorithmDecryptStringCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DecryptionKeyIDs mocks base method. +func (m *MockEncryptionAlgorithm) DecryptionKeyIDs() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecryptionKeyIDs") + ret0, _ := ret[0].([]string) + return ret0 +} + +// DecryptionKeyIDs indicates an expected call of DecryptionKeyIDs. +func (mr *MockEncryptionAlgorithmMockRecorder) DecryptionKeyIDs() *MockEncryptionAlgorithmDecryptionKeyIDsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptionKeyIDs", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).DecryptionKeyIDs)) + return &MockEncryptionAlgorithmDecryptionKeyIDsCall{Call: call} +} + +// MockEncryptionAlgorithmDecryptionKeyIDsCall wrap *gomock.Call +type MockEncryptionAlgorithmDecryptionKeyIDsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmDecryptionKeyIDsCall) Return(arg0 []string) *MockEncryptionAlgorithmDecryptionKeyIDsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmDecryptionKeyIDsCall) Do(f func() []string) *MockEncryptionAlgorithmDecryptionKeyIDsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmDecryptionKeyIDsCall) DoAndReturn(f func() []string) *MockEncryptionAlgorithmDecryptionKeyIDsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Encrypt mocks base method. +func (m *MockEncryptionAlgorithm) Encrypt(value []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Encrypt", value) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Encrypt indicates an expected call of Encrypt. +func (mr *MockEncryptionAlgorithmMockRecorder) Encrypt(value any) *MockEncryptionAlgorithmEncryptCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encrypt", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Encrypt), value) + return &MockEncryptionAlgorithmEncryptCall{Call: call} +} + +// MockEncryptionAlgorithmEncryptCall wrap *gomock.Call +type MockEncryptionAlgorithmEncryptCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmEncryptCall) Return(arg0 []byte, arg1 error) *MockEncryptionAlgorithmEncryptCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmEncryptCall) Do(f func([]byte) ([]byte, error)) *MockEncryptionAlgorithmEncryptCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmEncryptCall) DoAndReturn(f func([]byte) ([]byte, error)) *MockEncryptionAlgorithmEncryptCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// EncryptionKeyID mocks base method. +func (m *MockEncryptionAlgorithm) EncryptionKeyID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EncryptionKeyID") + ret0, _ := ret[0].(string) + return ret0 +} + +// EncryptionKeyID indicates an expected call of EncryptionKeyID. +func (mr *MockEncryptionAlgorithmMockRecorder) EncryptionKeyID() *MockEncryptionAlgorithmEncryptionKeyIDCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptionKeyID", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).EncryptionKeyID)) + return &MockEncryptionAlgorithmEncryptionKeyIDCall{Call: call} +} + +// MockEncryptionAlgorithmEncryptionKeyIDCall wrap *gomock.Call +type MockEncryptionAlgorithmEncryptionKeyIDCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockEncryptionAlgorithmEncryptionKeyIDCall) Return(arg0 string) *MockEncryptionAlgorithmEncryptionKeyIDCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockEncryptionAlgorithmEncryptionKeyIDCall) Do(f func() string) *MockEncryptionAlgorithmEncryptionKeyIDCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockEncryptionAlgorithmEncryptionKeyIDCall) DoAndReturn(f func() string) *MockEncryptionAlgorithmEncryptionKeyIDCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/internal/crypto/crypto_mock.go b/internal/crypto/crypto_mock.go deleted file mode 100644 index 4f2adea47..000000000 --- a/internal/crypto/crypto_mock.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: crypto.go -// -// Generated by this command: -// -// mockgen -source crypto.go -destination ./crypto_mock.go -package crypto -// - -// Package crypto is a generated GoMock package. -package crypto - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockEncryptionAlgorithm is a mock of EncryptionAlgorithm interface. -type MockEncryptionAlgorithm struct { - ctrl *gomock.Controller - recorder *MockEncryptionAlgorithmMockRecorder -} - -// MockEncryptionAlgorithmMockRecorder is the mock recorder for MockEncryptionAlgorithm. -type MockEncryptionAlgorithmMockRecorder struct { - mock *MockEncryptionAlgorithm -} - -// NewMockEncryptionAlgorithm creates a new mock instance. -func NewMockEncryptionAlgorithm(ctrl *gomock.Controller) *MockEncryptionAlgorithm { - mock := &MockEncryptionAlgorithm{ctrl: ctrl} - mock.recorder = &MockEncryptionAlgorithmMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEncryptionAlgorithm) EXPECT() *MockEncryptionAlgorithmMockRecorder { - return m.recorder -} - -// Algorithm mocks base method. -func (m *MockEncryptionAlgorithm) Algorithm() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Algorithm") - ret0, _ := ret[0].(string) - return ret0 -} - -// Algorithm indicates an expected call of Algorithm. -func (mr *MockEncryptionAlgorithmMockRecorder) Algorithm() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Algorithm", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Algorithm)) -} - -// Decrypt mocks base method. -func (m *MockEncryptionAlgorithm) Decrypt(hashed []byte, keyID string) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Decrypt", hashed, keyID) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Decrypt indicates an expected call of Decrypt. -func (mr *MockEncryptionAlgorithmMockRecorder) Decrypt(hashed, keyID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decrypt", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Decrypt), hashed, keyID) -} - -// DecryptString mocks base method. -func (m *MockEncryptionAlgorithm) DecryptString(hashed []byte, keyID string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DecryptString", hashed, keyID) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DecryptString indicates an expected call of DecryptString. -func (mr *MockEncryptionAlgorithmMockRecorder) DecryptString(hashed, keyID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptString", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).DecryptString), hashed, keyID) -} - -// DecryptionKeyIDs mocks base method. -func (m *MockEncryptionAlgorithm) DecryptionKeyIDs() []string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DecryptionKeyIDs") - ret0, _ := ret[0].([]string) - return ret0 -} - -// DecryptionKeyIDs indicates an expected call of DecryptionKeyIDs. -func (mr *MockEncryptionAlgorithmMockRecorder) DecryptionKeyIDs() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptionKeyIDs", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).DecryptionKeyIDs)) -} - -// Encrypt mocks base method. -func (m *MockEncryptionAlgorithm) Encrypt(value []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Encrypt", value) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Encrypt indicates an expected call of Encrypt. -func (mr *MockEncryptionAlgorithmMockRecorder) Encrypt(value any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encrypt", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).Encrypt), value) -} - -// EncryptionKeyID mocks base method. -func (m *MockEncryptionAlgorithm) EncryptionKeyID() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EncryptionKeyID") - ret0, _ := ret[0].(string) - return ret0 -} - -// EncryptionKeyID indicates an expected call of EncryptionKeyID. -func (mr *MockEncryptionAlgorithmMockRecorder) EncryptionKeyID() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptionKeyID", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).EncryptionKeyID)) -} diff --git a/internal/notification/handlers/user_notifier_test.go b/internal/notification/handlers/user_notifier_test.go index 258c7839b..f45ff7eea 100644 --- a/internal/notification/handlers/user_notifier_test.go +++ b/internal/notification/handlers/user_notifier_test.go @@ -2018,7 +2018,7 @@ func expectTemplateWithNotifyUserQueriesSMS(queries *mock.MockQueries) { queries.EXPECT().CustomTextListByTemplate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(&query.CustomTexts{}, nil) } -func cryptoValue(t *testing.T, ctrl *gomock.Controller, value string) (*crypto.MockEncryptionAlgorithm, *crypto.CryptoValue) { +func cryptoValue(t *testing.T, ctrl *gomock.Controller, value string) (crypto.EncryptionAlgorithm, *crypto.CryptoValue) { encAlg := crypto.NewMockEncryptionAlgorithm(ctrl) encAlg.EXPECT().Algorithm().AnyTimes().Return("enc") encAlg.EXPECT().EncryptionKeyID().AnyTimes().Return("id")