diff --git a/.github/instructions/nx.instructions.md b/.github/instructions/nx.instructions.md index b6b3c5e00..a798884dc 100644 --- a/.github/instructions/nx.instructions.md +++ b/.github/instructions/nx.instructions.md @@ -1,5 +1,6 @@ --- applyTo: '**' +excludeAgent: ["coding-agent", "code-review"] --- // This file is automatically generated by Nx Console diff --git a/backend/v3/domain/accesstokentype_enumer.go b/backend/v3/domain/accesstokentype_enumer.go new file mode 100644 index 000000000..421e4eac7 --- /dev/null +++ b/backend/v3/domain/accesstokentype_enumer.go @@ -0,0 +1,82 @@ +// Code generated by "enumer -type AccessTokenType -transform lower -trimprefix AccessTokenType"; DO NOT EDIT. + +package domain + +import ( + "fmt" + "strings" +) + +const _AccessTokenTypeName = "unspecifiedbearerjwt" + +var _AccessTokenTypeIndex = [...]uint8{0, 11, 17, 20} + +const _AccessTokenTypeLowerName = "unspecifiedbearerjwt" + +func (i AccessTokenType) String() string { + if i >= AccessTokenType(len(_AccessTokenTypeIndex)-1) { + return fmt.Sprintf("AccessTokenType(%d)", i) + } + return _AccessTokenTypeName[_AccessTokenTypeIndex[i]:_AccessTokenTypeIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _AccessTokenTypeNoOp() { + var x [1]struct{} + _ = x[AccessTokenTypeUnspecified-(0)] + _ = x[AccessTokenTypeBearer-(1)] + _ = x[AccessTokenTypeJWT-(2)] +} + +var _AccessTokenTypeValues = []AccessTokenType{AccessTokenTypeUnspecified, AccessTokenTypeBearer, AccessTokenTypeJWT} + +var _AccessTokenTypeNameToValueMap = map[string]AccessTokenType{ + _AccessTokenTypeName[0:11]: AccessTokenTypeUnspecified, + _AccessTokenTypeLowerName[0:11]: AccessTokenTypeUnspecified, + _AccessTokenTypeName[11:17]: AccessTokenTypeBearer, + _AccessTokenTypeLowerName[11:17]: AccessTokenTypeBearer, + _AccessTokenTypeName[17:20]: AccessTokenTypeJWT, + _AccessTokenTypeLowerName[17:20]: AccessTokenTypeJWT, +} + +var _AccessTokenTypeNames = []string{ + _AccessTokenTypeName[0:11], + _AccessTokenTypeName[11:17], + _AccessTokenTypeName[17:20], +} + +// AccessTokenTypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func AccessTokenTypeString(s string) (AccessTokenType, error) { + if val, ok := _AccessTokenTypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _AccessTokenTypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to AccessTokenType values", s) +} + +// AccessTokenTypeValues returns all values of the enum +func AccessTokenTypeValues() []AccessTokenType { + return _AccessTokenTypeValues +} + +// AccessTokenTypeStrings returns a slice of all String values of the enum +func AccessTokenTypeStrings() []string { + strs := make([]string, len(_AccessTokenTypeNames)) + copy(strs, _AccessTokenTypeNames) + return strs +} + +// IsAAccessTokenType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i AccessTokenType) IsAAccessTokenType() bool { + for _, v := range _AccessTokenTypeValues { + if i == v { + return true + } + } + return false +} diff --git a/backend/v3/domain/check.go b/backend/v3/domain/check.go deleted file mode 100644 index b594acc8d..000000000 --- a/backend/v3/domain/check.go +++ /dev/null @@ -1,71 +0,0 @@ -package domain - -import ( - "time" - - "github.com/zitadel/zitadel/internal/crypto" -) - -type Check struct { - Code *crypto.CryptoValue `db:"code"` - ExpiresAt *time.Time `db:"expires_at"` - FailedAttempts uint8 `db:"failed_attempts"` -} - -type CheckType interface { - isCheckType() -} - -type PasswordCheckType interface { - isPasswordCheckType() -} - -// CheckTypeInit initializes a new check. -type CheckTypeInit struct { - // CreatedAt is the time when the check was created. - // If zero, the current time will be used. - CreatedAt time.Time - // Expiry is the duration after which the check expires. - Expiry *time.Duration - // Code to be used for the check. - Code *crypto.CryptoValue -} - -// isCheckType implements [CheckType]. -func (v *CheckTypeInit) isCheckType() {} - -var _ CheckType = (*CheckTypeInit)(nil) - -// CheckTypeFailed indicates that the check failed. -// It is used to increment the failed attempts counter. -type CheckTypeFailed struct { - FailedAt time.Time -} - -// isCheckType implements [CheckType]. -func (v *CheckTypeFailed) isCheckType() {} - -// isPasswordCheckType implements [PasswordCheckType]. -func (v *CheckTypeFailed) isPasswordCheckType() {} - -var ( - _ CheckType = (*CheckTypeFailed)(nil) - _ PasswordCheckType = (*CheckTypeFailed)(nil) -) - -// CheckTypeSucceeded indicates that the check succeeded. -// It is used to reset the failed attempts counter. -type CheckTypeSucceeded struct { - SucceededAt time.Time -} - -// isCheckType implements [CheckType]. -func (v *CheckTypeSucceeded) isCheckType() {} - -// isPasswordCheckType implements [PasswordCheckType]. -func (v *CheckTypeSucceeded) isPasswordCheckType() {} - -var ( - _ CheckType = (*CheckTypeSucceeded)(nil) - _ PasswordCheckType = (*CheckTypeSucceeded)(nil) -) diff --git a/backend/v3/domain/machinekeytype_enumer.go b/backend/v3/domain/machinekeytype_enumer.go index aa423cdcf..7002bd7e2 100644 --- a/backend/v3/domain/machinekeytype_enumer.go +++ b/backend/v3/domain/machinekeytype_enumer.go @@ -7,11 +7,11 @@ import ( "strings" ) -const _MachineKeyTypeName = "unspecifiedjsonnone" +const _MachineKeyTypeName = "nonejson" -var _MachineKeyTypeIndex = [...]uint8{0, 11, 15, 19} +var _MachineKeyTypeIndex = [...]uint8{0, 4, 8} -const _MachineKeyTypeLowerName = "unspecifiedjsonnone" +const _MachineKeyTypeLowerName = "nonejson" func (i MachineKeyType) String() string { if i >= MachineKeyType(len(_MachineKeyTypeIndex)-1) { @@ -24,26 +24,22 @@ func (i MachineKeyType) String() string { // Re-run the stringer command to generate them again. func _MachineKeyTypeNoOp() { var x [1]struct{} - _ = x[MachineKeyTypeUnspecified-(0)] + _ = x[MachineKeyTypeNone-(0)] _ = x[MachineKeyTypeJSON-(1)] - _ = x[MachineKeyTypeNone-(2)] } -var _MachineKeyTypeValues = []MachineKeyType{MachineKeyTypeUnspecified, MachineKeyTypeJSON, MachineKeyTypeNone} +var _MachineKeyTypeValues = []MachineKeyType{MachineKeyTypeNone, MachineKeyTypeJSON} var _MachineKeyTypeNameToValueMap = map[string]MachineKeyType{ - _MachineKeyTypeName[0:11]: MachineKeyTypeUnspecified, - _MachineKeyTypeLowerName[0:11]: MachineKeyTypeUnspecified, - _MachineKeyTypeName[11:15]: MachineKeyTypeJSON, - _MachineKeyTypeLowerName[11:15]: MachineKeyTypeJSON, - _MachineKeyTypeName[15:19]: MachineKeyTypeNone, - _MachineKeyTypeLowerName[15:19]: MachineKeyTypeNone, + _MachineKeyTypeName[0:4]: MachineKeyTypeNone, + _MachineKeyTypeLowerName[0:4]: MachineKeyTypeNone, + _MachineKeyTypeName[4:8]: MachineKeyTypeJSON, + _MachineKeyTypeLowerName[4:8]: MachineKeyTypeJSON, } var _MachineKeyTypeNames = []string{ - _MachineKeyTypeName[0:11], - _MachineKeyTypeName[11:15], - _MachineKeyTypeName[15:19], + _MachineKeyTypeName[0:4], + _MachineKeyTypeName[4:8], } // MachineKeyTypeString retrieves an enum value from the enum constants string name. diff --git a/backend/v3/domain/mock/authorization.mock.go b/backend/v3/domain/mock/authorization.mock.go index d600fa9c8..66455bc1f 100644 --- a/backend/v3/domain/mock/authorization.mock.go +++ b/backend/v3/domain/mock/authorization.mock.go @@ -158,6 +158,44 @@ func (c *MockAuthorizationRepositoryDeleteCall) DoAndReturn(f func(context.Conte return c } +// ExistsRole mocks base method. +func (m *MockAuthorizationRepository) ExistsRole(cond database.Condition) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExistsRole", cond) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ExistsRole indicates an expected call of ExistsRole. +func (mr *MockAuthorizationRepositoryMockRecorder) ExistsRole(cond any) *MockAuthorizationRepositoryExistsRoleCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsRole", reflect.TypeOf((*MockAuthorizationRepository)(nil).ExistsRole), cond) + return &MockAuthorizationRepositoryExistsRoleCall{Call: call} +} + +// MockAuthorizationRepositoryExistsRoleCall wrap *gomock.Call +type MockAuthorizationRepositoryExistsRoleCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockAuthorizationRepositoryExistsRoleCall) Return(arg0 database.Condition) *MockAuthorizationRepositoryExistsRoleCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockAuthorizationRepositoryExistsRoleCall) Do(f func(database.Condition) database.Condition) *MockAuthorizationRepositoryExistsRoleCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockAuthorizationRepositoryExistsRoleCall) DoAndReturn(f func(database.Condition) database.Condition) *MockAuthorizationRepositoryExistsRoleCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // Get mocks base method. func (m *MockAuthorizationRepository) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.Authorization, error) { m.ctrl.T.Helper() diff --git a/backend/v3/domain/mock/human_user.mock.go b/backend/v3/domain/mock/human_user.mock.go index bb84ac124..40c6ac0bb 100644 --- a/backend/v3/domain/mock/human_user.mock.go +++ b/backend/v3/domain/mock/human_user.mock.go @@ -162,120 +162,6 @@ func (c *MockHumanUserRepositoryAddPasskeyCall) DoAndReturn(f func(*domain.Passk return c } -// CheckEmailOTP mocks base method. -func (m *MockHumanUserRepository) CheckEmailOTP(check domain.CheckType) database.Change { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckEmailOTP", check) - ret0, _ := ret[0].(database.Change) - return ret0 -} - -// CheckEmailOTP indicates an expected call of CheckEmailOTP. -func (mr *MockHumanUserRepositoryMockRecorder) CheckEmailOTP(check any) *MockHumanUserRepositoryCheckEmailOTPCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckEmailOTP", reflect.TypeOf((*MockHumanUserRepository)(nil).CheckEmailOTP), check) - return &MockHumanUserRepositoryCheckEmailOTPCall{Call: call} -} - -// MockHumanUserRepositoryCheckEmailOTPCall wrap *gomock.Call -type MockHumanUserRepositoryCheckEmailOTPCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryCheckEmailOTPCall) Return(arg0 database.Change) *MockHumanUserRepositoryCheckEmailOTPCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryCheckEmailOTPCall) Do(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckEmailOTPCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryCheckEmailOTPCall) DoAndReturn(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckEmailOTPCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// CheckSMSOTP mocks base method. -func (m *MockHumanUserRepository) CheckSMSOTP(check domain.CheckType) database.Change { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckSMSOTP", check) - ret0, _ := ret[0].(database.Change) - return ret0 -} - -// CheckSMSOTP indicates an expected call of CheckSMSOTP. -func (mr *MockHumanUserRepositoryMockRecorder) CheckSMSOTP(check any) *MockHumanUserRepositoryCheckSMSOTPCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckSMSOTP", reflect.TypeOf((*MockHumanUserRepository)(nil).CheckSMSOTP), check) - return &MockHumanUserRepositoryCheckSMSOTPCall{Call: call} -} - -// MockHumanUserRepositoryCheckSMSOTPCall wrap *gomock.Call -type MockHumanUserRepositoryCheckSMSOTPCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryCheckSMSOTPCall) Return(arg0 database.Change) *MockHumanUserRepositoryCheckSMSOTPCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryCheckSMSOTPCall) Do(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckSMSOTPCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryCheckSMSOTPCall) DoAndReturn(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckSMSOTPCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// CheckTOTP mocks base method. -func (m *MockHumanUserRepository) CheckTOTP(check domain.CheckType) database.Change { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckTOTP", check) - ret0, _ := ret[0].(database.Change) - return ret0 -} - -// CheckTOTP indicates an expected call of CheckTOTP. -func (mr *MockHumanUserRepositoryMockRecorder) CheckTOTP(check any) *MockHumanUserRepositoryCheckTOTPCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckTOTP", reflect.TypeOf((*MockHumanUserRepository)(nil).CheckTOTP), check) - return &MockHumanUserRepositoryCheckTOTPCall{Call: call} -} - -// MockHumanUserRepositoryCheckTOTPCall wrap *gomock.Call -type MockHumanUserRepositoryCheckTOTPCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryCheckTOTPCall) Return(arg0 database.Change) *MockHumanUserRepositoryCheckTOTPCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryCheckTOTPCall) Do(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckTOTPCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryCheckTOTPCall) DoAndReturn(f func(domain.CheckType) database.Change) *MockHumanUserRepositoryCheckTOTPCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - // CreatedAtColumn mocks base method. func (m *MockHumanUserRepository) CreatedAtColumn() database.Column { m.ctrl.T.Helper() @@ -694,6 +580,44 @@ func (c *MockHumanUserRepositoryEnableSMSOTPAtCall) DoAndReturn(f func(time.Time return c } +// ExistsIdentityProviderLink mocks base method. +func (m *MockHumanUserRepository) ExistsIdentityProviderLink(condition database.Condition) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExistsIdentityProviderLink", condition) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ExistsIdentityProviderLink indicates an expected call of ExistsIdentityProviderLink. +func (mr *MockHumanUserRepositoryMockRecorder) ExistsIdentityProviderLink(condition any) *MockHumanUserRepositoryExistsIdentityProviderLinkCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsIdentityProviderLink", reflect.TypeOf((*MockHumanUserRepository)(nil).ExistsIdentityProviderLink), condition) + return &MockHumanUserRepositoryExistsIdentityProviderLinkCall{Call: call} +} + +// MockHumanUserRepositoryExistsIdentityProviderLinkCall wrap *gomock.Call +type MockHumanUserRepositoryExistsIdentityProviderLinkCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryExistsIdentityProviderLinkCall) Return(arg0 database.Condition) *MockHumanUserRepositoryExistsIdentityProviderLinkCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryExistsIdentityProviderLinkCall) Do(f func(database.Condition) database.Condition) *MockHumanUserRepositoryExistsIdentityProviderLinkCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryExistsIdentityProviderLinkCall) DoAndReturn(f func(database.Condition) database.Condition) *MockHumanUserRepositoryExistsIdentityProviderLinkCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // ExistsMetadata mocks base method. func (m *MockHumanUserRepository) ExistsMetadata(condition database.Condition) database.Condition { m.ctrl.T.Helper() @@ -732,6 +656,44 @@ func (c *MockHumanUserRepositoryExistsMetadataCall) DoAndReturn(f func(database. return c } +// ExistsPasskey mocks base method. +func (m *MockHumanUserRepository) ExistsPasskey(condition database.Condition) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExistsPasskey", condition) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ExistsPasskey indicates an expected call of ExistsPasskey. +func (mr *MockHumanUserRepositoryMockRecorder) ExistsPasskey(condition any) *MockHumanUserRepositoryExistsPasskeyCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsPasskey", reflect.TypeOf((*MockHumanUserRepository)(nil).ExistsPasskey), condition) + return &MockHumanUserRepositoryExistsPasskeyCall{Call: call} +} + +// MockHumanUserRepositoryExistsPasskeyCall wrap *gomock.Call +type MockHumanUserRepositoryExistsPasskeyCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryExistsPasskeyCall) Return(arg0 database.Condition) *MockHumanUserRepositoryExistsPasskeyCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryExistsPasskeyCall) Do(f func(database.Condition) database.Condition) *MockHumanUserRepositoryExistsPasskeyCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryExistsPasskeyCall) DoAndReturn(f func(database.Condition) database.Condition) *MockHumanUserRepositoryExistsPasskeyCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // FirstNameColumn mocks base method. func (m *MockHumanUserRepository) FirstNameColumn() database.Column { m.ctrl.T.Helper() @@ -808,6 +770,44 @@ func (c *MockHumanUserRepositoryFirstNameConditionCall) DoAndReturn(f func(datab return c } +// IDColumn mocks base method. +func (m *MockHumanUserRepository) IDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// IDColumn indicates an expected call of IDColumn. +func (mr *MockHumanUserRepositoryMockRecorder) IDColumn() *MockHumanUserRepositoryIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDColumn", reflect.TypeOf((*MockHumanUserRepository)(nil).IDColumn)) + return &MockHumanUserRepositoryIDColumnCall{Call: call} +} + +// MockHumanUserRepositoryIDColumnCall wrap *gomock.Call +type MockHumanUserRepositoryIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryIDColumnCall) Return(arg0 database.Column) *MockHumanUserRepositoryIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryIDColumnCall) Do(f func() database.Column) *MockHumanUserRepositoryIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryIDColumnCall) DoAndReturn(f func() database.Column) *MockHumanUserRepositoryIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // IDCondition mocks base method. func (m *MockHumanUserRepository) IDCondition(userID string) database.Condition { m.ctrl.T.Helper() @@ -846,40 +846,192 @@ func (c *MockHumanUserRepositoryIDConditionCall) DoAndReturn(f func(string) data return c } -// IdentityProviderIDCondition mocks base method. -func (m *MockHumanUserRepository) IdentityProviderIDCondition(idpID string) database.Condition { +// IdentityProviderLinkConditions mocks base method. +func (m *MockHumanUserRepository) IdentityProviderLinkConditions() domain.HumanIdentityProviderLinkConditions { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IdentityProviderIDCondition", idpID) - ret0, _ := ret[0].(database.Condition) + ret := m.ctrl.Call(m, "IdentityProviderLinkConditions") + ret0, _ := ret[0].(domain.HumanIdentityProviderLinkConditions) return ret0 } -// IdentityProviderIDCondition indicates an expected call of IdentityProviderIDCondition. -func (mr *MockHumanUserRepositoryMockRecorder) IdentityProviderIDCondition(idpID any) *MockHumanUserRepositoryIdentityProviderIDConditionCall { +// IdentityProviderLinkConditions indicates an expected call of IdentityProviderLinkConditions. +func (mr *MockHumanUserRepositoryMockRecorder) IdentityProviderLinkConditions() *MockHumanUserRepositoryIdentityProviderLinkConditionsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IdentityProviderIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).IdentityProviderIDCondition), idpID) - return &MockHumanUserRepositoryIdentityProviderIDConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IdentityProviderLinkConditions", reflect.TypeOf((*MockHumanUserRepository)(nil).IdentityProviderLinkConditions)) + return &MockHumanUserRepositoryIdentityProviderLinkConditionsCall{Call: call} } -// MockHumanUserRepositoryIdentityProviderIDConditionCall wrap *gomock.Call -type MockHumanUserRepositoryIdentityProviderIDConditionCall struct { +// MockHumanUserRepositoryIdentityProviderLinkConditionsCall wrap *gomock.Call +type MockHumanUserRepositoryIdentityProviderLinkConditionsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryIdentityProviderIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryIdentityProviderIDConditionCall { +func (c *MockHumanUserRepositoryIdentityProviderLinkConditionsCall) Return(arg0 domain.HumanIdentityProviderLinkConditions) *MockHumanUserRepositoryIdentityProviderLinkConditionsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryIdentityProviderIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryIdentityProviderIDConditionCall { +func (c *MockHumanUserRepositoryIdentityProviderLinkConditionsCall) Do(f func() domain.HumanIdentityProviderLinkConditions) *MockHumanUserRepositoryIdentityProviderLinkConditionsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryIdentityProviderIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryIdentityProviderIDConditionCall { +func (c *MockHumanUserRepositoryIdentityProviderLinkConditionsCall) DoAndReturn(f func() domain.HumanIdentityProviderLinkConditions) *MockHumanUserRepositoryIdentityProviderLinkConditionsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IncrementEmailOTPFailedAttempts mocks base method. +func (m *MockHumanUserRepository) IncrementEmailOTPFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncrementEmailOTPFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// IncrementEmailOTPFailedAttempts indicates an expected call of IncrementEmailOTPFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) IncrementEmailOTPFailedAttempts() *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementEmailOTPFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).IncrementEmailOTPFailedAttempts)) + return &MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryIncrementEmailOTPFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IncrementPasswordFailedAttempts mocks base method. +func (m *MockHumanUserRepository) IncrementPasswordFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncrementPasswordFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// IncrementPasswordFailedAttempts indicates an expected call of IncrementPasswordFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) IncrementPasswordFailedAttempts() *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementPasswordFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).IncrementPasswordFailedAttempts)) + return &MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryIncrementPasswordFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IncrementSMSOTPFailedAttempts mocks base method. +func (m *MockHumanUserRepository) IncrementSMSOTPFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IncrementSMSOTPFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// IncrementSMSOTPFailedAttempts indicates an expected call of IncrementSMSOTPFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) IncrementSMSOTPFailedAttempts() *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementSMSOTPFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).IncrementSMSOTPFailedAttempts)) + return &MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryIncrementSMSOTPFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// InstanceIDColumn mocks base method. +func (m *MockHumanUserRepository) InstanceIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// InstanceIDColumn indicates an expected call of InstanceIDColumn. +func (mr *MockHumanUserRepositoryMockRecorder) InstanceIDColumn() *MockHumanUserRepositoryInstanceIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDColumn", reflect.TypeOf((*MockHumanUserRepository)(nil).InstanceIDColumn)) + return &MockHumanUserRepositoryInstanceIDColumnCall{Call: call} +} + +// MockHumanUserRepositoryInstanceIDColumnCall wrap *gomock.Call +type MockHumanUserRepositoryInstanceIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryInstanceIDColumnCall) Return(arg0 database.Column) *MockHumanUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryInstanceIDColumnCall) Do(f func() database.Column) *MockHumanUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryInstanceIDColumnCall) DoAndReturn(f func() database.Column) *MockHumanUserRepositoryInstanceIDColumnCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -1036,78 +1188,40 @@ func (c *MockHumanUserRepositoryLoginNameConditionCall) DoAndReturn(f func(datab return c } -// MetadataKeyCondition mocks base method. -func (m *MockHumanUserRepository) MetadataKeyCondition(key string) database.Condition { +// MetadataConditions mocks base method. +func (m *MockHumanUserRepository) MetadataConditions() domain.UserMetadataConditions { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataKeyCondition", key) - ret0, _ := ret[0].(database.Condition) + ret := m.ctrl.Call(m, "MetadataConditions") + ret0, _ := ret[0].(domain.UserMetadataConditions) return ret0 } -// MetadataKeyCondition indicates an expected call of MetadataKeyCondition. -func (mr *MockHumanUserRepositoryMockRecorder) MetadataKeyCondition(key any) *MockHumanUserRepositoryMetadataKeyConditionCall { +// MetadataConditions indicates an expected call of MetadataConditions. +func (mr *MockHumanUserRepositoryMockRecorder) MetadataConditions() *MockHumanUserRepositoryMetadataConditionsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataKeyCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).MetadataKeyCondition), key) - return &MockHumanUserRepositoryMetadataKeyConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataConditions", reflect.TypeOf((*MockHumanUserRepository)(nil).MetadataConditions)) + return &MockHumanUserRepositoryMetadataConditionsCall{Call: call} } -// MockHumanUserRepositoryMetadataKeyConditionCall wrap *gomock.Call -type MockHumanUserRepositoryMetadataKeyConditionCall struct { +// MockHumanUserRepositoryMetadataConditionsCall wrap *gomock.Call +type MockHumanUserRepositoryMetadataConditionsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryMetadataKeyConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryMetadataKeyConditionCall { +func (c *MockHumanUserRepositoryMetadataConditionsCall) Return(arg0 domain.UserMetadataConditions) *MockHumanUserRepositoryMetadataConditionsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryMetadataKeyConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryMetadataKeyConditionCall { +func (c *MockHumanUserRepositoryMetadataConditionsCall) Do(f func() domain.UserMetadataConditions) *MockHumanUserRepositoryMetadataConditionsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryMetadataKeyConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryMetadataKeyConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// MetadataValueCondition mocks base method. -func (m *MockHumanUserRepository) MetadataValueCondition(op database.BytesOperation, value []byte) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataValueCondition", op, value) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// MetadataValueCondition indicates an expected call of MetadataValueCondition. -func (mr *MockHumanUserRepositoryMockRecorder) MetadataValueCondition(op, value any) *MockHumanUserRepositoryMetadataValueConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataValueCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).MetadataValueCondition), op, value) - return &MockHumanUserRepositoryMetadataValueConditionCall{Call: call} -} - -// MockHumanUserRepositoryMetadataValueConditionCall wrap *gomock.Call -type MockHumanUserRepositoryMetadataValueConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryMetadataValueConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryMetadataValueConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryMetadataValueConditionCall) Do(f func(database.BytesOperation, []byte) database.Condition) *MockHumanUserRepositoryMetadataValueConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryMetadataValueConditionCall) DoAndReturn(f func(database.BytesOperation, []byte) database.Condition) *MockHumanUserRepositoryMetadataValueConditionCall { +func (c *MockHumanUserRepositoryMetadataConditionsCall) DoAndReturn(f func() domain.UserMetadataConditions) *MockHumanUserRepositoryMetadataConditionsCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -1188,154 +1302,78 @@ func (c *MockHumanUserRepositoryNicknameConditionCall) DoAndReturn(f func(databa return c } -// OrgIDCondition mocks base method. -func (m *MockHumanUserRepository) OrgIDCondition(orgID string) database.Condition { +// OrganizationIDCondition mocks base method. +func (m *MockHumanUserRepository) OrganizationIDCondition(orgID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OrgIDCondition", orgID) + ret := m.ctrl.Call(m, "OrganizationIDCondition", orgID) ret0, _ := ret[0].(database.Condition) return ret0 } -// OrgIDCondition indicates an expected call of OrgIDCondition. -func (mr *MockHumanUserRepositoryMockRecorder) OrgIDCondition(orgID any) *MockHumanUserRepositoryOrgIDConditionCall { +// OrganizationIDCondition indicates an expected call of OrganizationIDCondition. +func (mr *MockHumanUserRepositoryMockRecorder) OrganizationIDCondition(orgID any) *MockHumanUserRepositoryOrganizationIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrgIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).OrgIDCondition), orgID) - return &MockHumanUserRepositoryOrgIDConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrganizationIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).OrganizationIDCondition), orgID) + return &MockHumanUserRepositoryOrganizationIDConditionCall{Call: call} } -// MockHumanUserRepositoryOrgIDConditionCall wrap *gomock.Call -type MockHumanUserRepositoryOrgIDConditionCall struct { +// MockHumanUserRepositoryOrganizationIDConditionCall wrap *gomock.Call +type MockHumanUserRepositoryOrganizationIDConditionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryOrgIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryOrgIDConditionCall { +func (c *MockHumanUserRepositoryOrganizationIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryOrgIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryOrgIDConditionCall { +func (c *MockHumanUserRepositoryOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryOrgIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryOrgIDConditionCall { +func (c *MockHumanUserRepositoryOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.DoAndReturn(f) return c } -// PasskeyChallengeCondition mocks base method. -func (m *MockHumanUserRepository) PasskeyChallengeCondition(challenge string) database.Condition { +// PasskeyConditions mocks base method. +func (m *MockHumanUserRepository) PasskeyConditions() domain.HumanPasskeyConditions { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasskeyChallengeCondition", challenge) - ret0, _ := ret[0].(database.Condition) + ret := m.ctrl.Call(m, "PasskeyConditions") + ret0, _ := ret[0].(domain.HumanPasskeyConditions) return ret0 } -// PasskeyChallengeCondition indicates an expected call of PasskeyChallengeCondition. -func (mr *MockHumanUserRepositoryMockRecorder) PasskeyChallengeCondition(challenge any) *MockHumanUserRepositoryPasskeyChallengeConditionCall { +// PasskeyConditions indicates an expected call of PasskeyConditions. +func (mr *MockHumanUserRepositoryMockRecorder) PasskeyConditions() *MockHumanUserRepositoryPasskeyConditionsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasskeyChallengeCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).PasskeyChallengeCondition), challenge) - return &MockHumanUserRepositoryPasskeyChallengeConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasskeyConditions", reflect.TypeOf((*MockHumanUserRepository)(nil).PasskeyConditions)) + return &MockHumanUserRepositoryPasskeyConditionsCall{Call: call} } -// MockHumanUserRepositoryPasskeyChallengeConditionCall wrap *gomock.Call -type MockHumanUserRepositoryPasskeyChallengeConditionCall struct { +// MockHumanUserRepositoryPasskeyConditionsCall wrap *gomock.Call +type MockHumanUserRepositoryPasskeyConditionsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryPasskeyChallengeConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryPasskeyChallengeConditionCall { +func (c *MockHumanUserRepositoryPasskeyConditionsCall) Return(arg0 domain.HumanPasskeyConditions) *MockHumanUserRepositoryPasskeyConditionsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryPasskeyChallengeConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyChallengeConditionCall { +func (c *MockHumanUserRepositoryPasskeyConditionsCall) Do(f func() domain.HumanPasskeyConditions) *MockHumanUserRepositoryPasskeyConditionsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryPasskeyChallengeConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyChallengeConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// PasskeyIDCondition mocks base method. -func (m *MockHumanUserRepository) PasskeyIDCondition(passkeyID string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasskeyIDCondition", passkeyID) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// PasskeyIDCondition indicates an expected call of PasskeyIDCondition. -func (mr *MockHumanUserRepositoryMockRecorder) PasskeyIDCondition(passkeyID any) *MockHumanUserRepositoryPasskeyIDConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasskeyIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).PasskeyIDCondition), passkeyID) - return &MockHumanUserRepositoryPasskeyIDConditionCall{Call: call} -} - -// MockHumanUserRepositoryPasskeyIDConditionCall wrap *gomock.Call -type MockHumanUserRepositoryPasskeyIDConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryPasskeyIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryPasskeyIDConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryPasskeyIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyIDConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryPasskeyIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyIDConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// PasskeyKeyIDCondition mocks base method. -func (m *MockHumanUserRepository) PasskeyKeyIDCondition(keyID string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasskeyKeyIDCondition", keyID) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// PasskeyKeyIDCondition indicates an expected call of PasskeyKeyIDCondition. -func (mr *MockHumanUserRepositoryMockRecorder) PasskeyKeyIDCondition(keyID any) *MockHumanUserRepositoryPasskeyKeyIDConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasskeyKeyIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).PasskeyKeyIDCondition), keyID) - return &MockHumanUserRepositoryPasskeyKeyIDConditionCall{Call: call} -} - -// MockHumanUserRepositoryPasskeyKeyIDConditionCall wrap *gomock.Call -type MockHumanUserRepositoryPasskeyKeyIDConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryPasskeyKeyIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryPasskeyKeyIDConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryPasskeyKeyIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyKeyIDConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryPasskeyKeyIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryPasskeyKeyIDConditionCall { +func (c *MockHumanUserRepositoryPasskeyConditionsCall) DoAndReturn(f func() domain.HumanPasskeyConditions) *MockHumanUserRepositoryPasskeyConditionsCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -1492,82 +1530,6 @@ func (c *MockHumanUserRepositoryPrimaryKeyConditionCall) DoAndReturn(f func(stri return c } -// ProvidedUserIDCondition mocks base method. -func (m *MockHumanUserRepository) ProvidedUserIDCondition(providedUserID string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProvidedUserIDCondition", providedUserID) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// ProvidedUserIDCondition indicates an expected call of ProvidedUserIDCondition. -func (mr *MockHumanUserRepositoryMockRecorder) ProvidedUserIDCondition(providedUserID any) *MockHumanUserRepositoryProvidedUserIDConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedUserIDCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).ProvidedUserIDCondition), providedUserID) - return &MockHumanUserRepositoryProvidedUserIDConditionCall{Call: call} -} - -// MockHumanUserRepositoryProvidedUserIDConditionCall wrap *gomock.Call -type MockHumanUserRepositoryProvidedUserIDConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryProvidedUserIDConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryProvidedUserIDConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryProvidedUserIDConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryProvidedUserIDConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryProvidedUserIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryProvidedUserIDConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// ProvidedUsernameCondition mocks base method. -func (m *MockHumanUserRepository) ProvidedUsernameCondition(username string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProvidedUsernameCondition", username) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// ProvidedUsernameCondition indicates an expected call of ProvidedUsernameCondition. -func (mr *MockHumanUserRepositoryMockRecorder) ProvidedUsernameCondition(username any) *MockHumanUserRepositoryProvidedUsernameConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedUsernameCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).ProvidedUsernameCondition), username) - return &MockHumanUserRepositoryProvidedUsernameConditionCall{Call: call} -} - -// MockHumanUserRepositoryProvidedUsernameConditionCall wrap *gomock.Call -type MockHumanUserRepositoryProvidedUsernameConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositoryProvidedUsernameConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryProvidedUsernameConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryProvidedUsernameConditionCall) Do(f func(string) database.Condition) *MockHumanUserRepositoryProvidedUsernameConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryProvidedUsernameConditionCall) DoAndReturn(f func(string) database.Condition) *MockHumanUserRepositoryProvidedUsernameConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - // RemoveIdentityProviderLink mocks base method. func (m *MockHumanUserRepository) RemoveIdentityProviderLink(providerID, providedUserID string) database.Change { m.ctrl.T.Helper() @@ -1758,6 +1720,120 @@ func (c *MockHumanUserRepositoryRemoveTOTPCall) DoAndReturn(f func() database.Ch return c } +// ResetEmailOTPFailedAttempts mocks base method. +func (m *MockHumanUserRepository) ResetEmailOTPFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetEmailOTPFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// ResetEmailOTPFailedAttempts indicates an expected call of ResetEmailOTPFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) ResetEmailOTPFailedAttempts() *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetEmailOTPFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).ResetEmailOTPFailedAttempts)) + return &MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryResetEmailOTPFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ResetPasswordFailedAttempts mocks base method. +func (m *MockHumanUserRepository) ResetPasswordFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetPasswordFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// ResetPasswordFailedAttempts indicates an expected call of ResetPasswordFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) ResetPasswordFailedAttempts() *MockHumanUserRepositoryResetPasswordFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetPasswordFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).ResetPasswordFailedAttempts)) + return &MockHumanUserRepositoryResetPasswordFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryResetPasswordFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryResetPasswordFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryResetPasswordFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryResetPasswordFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryResetPasswordFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryResetPasswordFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryResetPasswordFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryResetPasswordFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ResetSMSOTPFailedAttempts mocks base method. +func (m *MockHumanUserRepository) ResetSMSOTPFailedAttempts() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResetSMSOTPFailedAttempts") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// ResetSMSOTPFailedAttempts indicates an expected call of ResetSMSOTPFailedAttempts. +func (mr *MockHumanUserRepositoryMockRecorder) ResetSMSOTPFailedAttempts() *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetSMSOTPFailedAttempts", reflect.TypeOf((*MockHumanUserRepository)(nil).ResetSMSOTPFailedAttempts)) + return &MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall{Call: call} +} + +// MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall wrap *gomock.Call +type MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall) Return(arg0 database.Change) *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall) Do(f func() database.Change) *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositoryResetSMSOTPFailedAttemptsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // SetAvatarKey mocks base method. func (m *MockHumanUserRepository) SetAvatarKey(avatarKey *string) database.Change { m.ctrl.T.Helper() @@ -1835,17 +1911,17 @@ func (c *MockHumanUserRepositorySetDisplayNameCall) DoAndReturn(f func(string) d } // SetEmail mocks base method. -func (m *MockHumanUserRepository) SetEmail(verification domain.VerificationType) database.Change { +func (m *MockHumanUserRepository) SetEmail(address string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetEmail", verification) + ret := m.ctrl.Call(m, "SetEmail", address) ret0, _ := ret[0].(database.Change) return ret0 } // SetEmail indicates an expected call of SetEmail. -func (mr *MockHumanUserRepositoryMockRecorder) SetEmail(verification any) *MockHumanUserRepositorySetEmailCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetEmail(address any) *MockHumanUserRepositorySetEmailCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEmail", reflect.TypeOf((*MockHumanUserRepository)(nil).SetEmail), verification) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEmail", reflect.TypeOf((*MockHumanUserRepository)(nil).SetEmail), address) return &MockHumanUserRepositorySetEmailCall{Call: call} } @@ -1861,13 +1937,51 @@ func (c *MockHumanUserRepositorySetEmailCall) Return(arg0 database.Change) *Mock } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetEmailCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetEmailCall { +func (c *MockHumanUserRepositorySetEmailCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetEmailCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetEmailCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetEmailCall { +func (c *MockHumanUserRepositorySetEmailCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetEmailCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetEmailVerification mocks base method. +func (m *MockHumanUserRepository) SetEmailVerification(verification domain.VerificationType) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetEmailVerification", verification) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetEmailVerification indicates an expected call of SetEmailVerification. +func (mr *MockHumanUserRepositoryMockRecorder) SetEmailVerification(verification any) *MockHumanUserRepositorySetEmailVerificationCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEmailVerification", reflect.TypeOf((*MockHumanUserRepository)(nil).SetEmailVerification), verification) + return &MockHumanUserRepositorySetEmailVerificationCall{Call: call} +} + +// MockHumanUserRepositorySetEmailVerificationCall wrap *gomock.Call +type MockHumanUserRepositorySetEmailVerificationCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetEmailVerificationCall) Return(arg0 database.Change) *MockHumanUserRepositorySetEmailVerificationCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetEmailVerificationCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetEmailVerificationCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetEmailVerificationCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetEmailVerificationCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -1949,17 +2063,17 @@ func (c *MockHumanUserRepositorySetGenderCall) DoAndReturn(f func(domain.HumanGe } // SetIdentityProviderLinkProvidedID mocks base method. -func (m *MockHumanUserRepository) SetIdentityProviderLinkProvidedID(providerID, currentProvidedUserID, newProvidedUserID string) database.Change { +func (m *MockHumanUserRepository) SetIdentityProviderLinkProvidedID(providedUserID string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetIdentityProviderLinkProvidedID", providerID, currentProvidedUserID, newProvidedUserID) + ret := m.ctrl.Call(m, "SetIdentityProviderLinkProvidedID", providedUserID) ret0, _ := ret[0].(database.Change) return ret0 } // SetIdentityProviderLinkProvidedID indicates an expected call of SetIdentityProviderLinkProvidedID. -func (mr *MockHumanUserRepositoryMockRecorder) SetIdentityProviderLinkProvidedID(providerID, currentProvidedUserID, newProvidedUserID any) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetIdentityProviderLinkProvidedID(providedUserID any) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIdentityProviderLinkProvidedID", reflect.TypeOf((*MockHumanUserRepository)(nil).SetIdentityProviderLinkProvidedID), providerID, currentProvidedUserID, newProvidedUserID) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIdentityProviderLinkProvidedID", reflect.TypeOf((*MockHumanUserRepository)(nil).SetIdentityProviderLinkProvidedID), providedUserID) return &MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall{Call: call} } @@ -1975,29 +2089,29 @@ func (c *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall) Return(ar } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall) Do(f func(string, string, string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { +func (c *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall) DoAndReturn(f func(string, string, string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { +func (c *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkProvidedIDCall { c.Call = c.Call.DoAndReturn(f) return c } // SetIdentityProviderLinkUsername mocks base method. -func (m *MockHumanUserRepository) SetIdentityProviderLinkUsername(providerID, providedUserID, username string) database.Change { +func (m *MockHumanUserRepository) SetIdentityProviderLinkUsername(username string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetIdentityProviderLinkUsername", providerID, providedUserID, username) + ret := m.ctrl.Call(m, "SetIdentityProviderLinkUsername", username) ret0, _ := ret[0].(database.Change) return ret0 } // SetIdentityProviderLinkUsername indicates an expected call of SetIdentityProviderLinkUsername. -func (mr *MockHumanUserRepositoryMockRecorder) SetIdentityProviderLinkUsername(providerID, providedUserID, username any) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetIdentityProviderLinkUsername(username any) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIdentityProviderLinkUsername", reflect.TypeOf((*MockHumanUserRepository)(nil).SetIdentityProviderLinkUsername), providerID, providedUserID, username) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIdentityProviderLinkUsername", reflect.TypeOf((*MockHumanUserRepository)(nil).SetIdentityProviderLinkUsername), username) return &MockHumanUserRepositorySetIdentityProviderLinkUsernameCall{Call: call} } @@ -2013,13 +2127,13 @@ func (c *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall) Return(arg0 } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall) Do(f func(string, string, string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { +func (c *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall) DoAndReturn(f func(string, string, string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { +func (c *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetIdentityProviderLinkUsernameCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2062,40 +2176,154 @@ func (c *MockHumanUserRepositorySetLastNameCall) DoAndReturn(f func(string) data return c } -// SetMultifactorInitializationSkippedAt mocks base method. -func (m *MockHumanUserRepository) SetMultifactorInitializationSkippedAt(skippedAt time.Time) database.Change { +// SetLastSuccessfulEmailOTPCheck mocks base method. +func (m *MockHumanUserRepository) SetLastSuccessfulEmailOTPCheck(checkedAt time.Time) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetMultifactorInitializationSkippedAt", skippedAt) + ret := m.ctrl.Call(m, "SetLastSuccessfulEmailOTPCheck", checkedAt) ret0, _ := ret[0].(database.Change) return ret0 } -// SetMultifactorInitializationSkippedAt indicates an expected call of SetMultifactorInitializationSkippedAt. -func (mr *MockHumanUserRepositoryMockRecorder) SetMultifactorInitializationSkippedAt(skippedAt any) *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall { +// SetLastSuccessfulEmailOTPCheck indicates an expected call of SetLastSuccessfulEmailOTPCheck. +func (mr *MockHumanUserRepositoryMockRecorder) SetLastSuccessfulEmailOTPCheck(checkedAt any) *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMultifactorInitializationSkippedAt", reflect.TypeOf((*MockHumanUserRepository)(nil).SetMultifactorInitializationSkippedAt), skippedAt) - return &MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastSuccessfulEmailOTPCheck", reflect.TypeOf((*MockHumanUserRepository)(nil).SetLastSuccessfulEmailOTPCheck), checkedAt) + return &MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall{Call: call} } -// MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall wrap *gomock.Call -type MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall struct { +// MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall wrap *gomock.Call +type MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall) Return(arg0 database.Change) *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall { +func (c *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall) Return(arg0 database.Change) *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall { +func (c *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetMultifactorInitializationSkippedAtCall { +func (c *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulEmailOTPCheckCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetLastSuccessfulPasswordCheck mocks base method. +func (m *MockHumanUserRepository) SetLastSuccessfulPasswordCheck(checkedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLastSuccessfulPasswordCheck", checkedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetLastSuccessfulPasswordCheck indicates an expected call of SetLastSuccessfulPasswordCheck. +func (mr *MockHumanUserRepositoryMockRecorder) SetLastSuccessfulPasswordCheck(checkedAt any) *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastSuccessfulPasswordCheck", reflect.TypeOf((*MockHumanUserRepository)(nil).SetLastSuccessfulPasswordCheck), checkedAt) + return &MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall{Call: call} +} + +// MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall wrap *gomock.Call +type MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall) Return(arg0 database.Change) *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulPasswordCheckCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetLastSuccessfulSMSOTPCheck mocks base method. +func (m *MockHumanUserRepository) SetLastSuccessfulSMSOTPCheck(checkedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLastSuccessfulSMSOTPCheck", checkedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetLastSuccessfulSMSOTPCheck indicates an expected call of SetLastSuccessfulSMSOTPCheck. +func (mr *MockHumanUserRepositoryMockRecorder) SetLastSuccessfulSMSOTPCheck(checkedAt any) *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastSuccessfulSMSOTPCheck", reflect.TypeOf((*MockHumanUserRepository)(nil).SetLastSuccessfulSMSOTPCheck), checkedAt) + return &MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall{Call: call} +} + +// MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall wrap *gomock.Call +type MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall) Return(arg0 database.Change) *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulSMSOTPCheckCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetLastSuccessfulTOTPCheck mocks base method. +func (m *MockHumanUserRepository) SetLastSuccessfulTOTPCheck(checkedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLastSuccessfulTOTPCheck", checkedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetLastSuccessfulTOTPCheck indicates an expected call of SetLastSuccessfulTOTPCheck. +func (mr *MockHumanUserRepositoryMockRecorder) SetLastSuccessfulTOTPCheck(checkedAt any) *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastSuccessfulTOTPCheck", reflect.TypeOf((*MockHumanUserRepository)(nil).SetLastSuccessfulTOTPCheck), checkedAt) + return &MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall{Call: call} +} + +// MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall wrap *gomock.Call +type MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall) Return(arg0 database.Change) *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetLastSuccessfulTOTPCheckCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2443,17 +2671,17 @@ func (c *MockHumanUserRepositorySetPasskeyVerifiedAtCall) DoAndReturn(f func(tim } // SetPassword mocks base method. -func (m *MockHumanUserRepository) SetPassword(verification domain.VerificationType) database.Change { +func (m *MockHumanUserRepository) SetPassword(hash string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetPassword", verification) + ret := m.ctrl.Call(m, "SetPassword", hash) ret0, _ := ret[0].(database.Change) return ret0 } // SetPassword indicates an expected call of SetPassword. -func (mr *MockHumanUserRepositoryMockRecorder) SetPassword(verification any) *MockHumanUserRepositorySetPasswordCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetPassword(hash any) *MockHumanUserRepositorySetPasswordCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPassword", reflect.TypeOf((*MockHumanUserRepository)(nil).SetPassword), verification) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPassword", reflect.TypeOf((*MockHumanUserRepository)(nil).SetPassword), hash) return &MockHumanUserRepositorySetPasswordCall{Call: call} } @@ -2469,13 +2697,13 @@ func (c *MockHumanUserRepositorySetPasswordCall) Return(arg0 database.Change) *M } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetPasswordCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPasswordCall { +func (c *MockHumanUserRepositorySetPasswordCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetPasswordCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetPasswordCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPasswordCall { +func (c *MockHumanUserRepositorySetPasswordCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetPasswordCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2519,17 +2747,17 @@ func (c *MockHumanUserRepositorySetPasswordChangeRequiredCall) DoAndReturn(f fun } // SetPhone mocks base method. -func (m *MockHumanUserRepository) SetPhone(verification domain.VerificationType) database.Change { +func (m *MockHumanUserRepository) SetPhone(number string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetPhone", verification) + ret := m.ctrl.Call(m, "SetPhone", number) ret0, _ := ret[0].(database.Change) return ret0 } // SetPhone indicates an expected call of SetPhone. -func (mr *MockHumanUserRepositoryMockRecorder) SetPhone(verification any) *MockHumanUserRepositorySetPhoneCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetPhone(number any) *MockHumanUserRepositorySetPhoneCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhone", reflect.TypeOf((*MockHumanUserRepository)(nil).SetPhone), verification) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhone", reflect.TypeOf((*MockHumanUserRepository)(nil).SetPhone), number) return &MockHumanUserRepositorySetPhoneCall{Call: call} } @@ -2545,13 +2773,51 @@ func (c *MockHumanUserRepositorySetPhoneCall) Return(arg0 database.Change) *Mock } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetPhoneCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPhoneCall { +func (c *MockHumanUserRepositorySetPhoneCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetPhoneCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetPhoneCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPhoneCall { +func (c *MockHumanUserRepositorySetPhoneCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetPhoneCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetPhoneVerification mocks base method. +func (m *MockHumanUserRepository) SetPhoneVerification(verification domain.VerificationType) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetPhoneVerification", verification) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetPhoneVerification indicates an expected call of SetPhoneVerification. +func (mr *MockHumanUserRepositoryMockRecorder) SetPhoneVerification(verification any) *MockHumanUserRepositorySetPhoneVerificationCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPhoneVerification", reflect.TypeOf((*MockHumanUserRepository)(nil).SetPhoneVerification), verification) + return &MockHumanUserRepositorySetPhoneVerificationCall{Call: call} +} + +// MockHumanUserRepositorySetPhoneVerificationCall wrap *gomock.Call +type MockHumanUserRepositorySetPhoneVerificationCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetPhoneVerificationCall) Return(arg0 database.Change) *MockHumanUserRepositorySetPhoneVerificationCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetPhoneVerificationCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPhoneVerificationCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetPhoneVerificationCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetPhoneVerificationCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2594,6 +2860,44 @@ func (c *MockHumanUserRepositorySetPreferredLanguageCall) DoAndReturn(f func(lan return c } +// SetResetPasswordVerification mocks base method. +func (m *MockHumanUserRepository) SetResetPasswordVerification(verification domain.VerificationType) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetResetPasswordVerification", verification) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetResetPasswordVerification indicates an expected call of SetResetPasswordVerification. +func (mr *MockHumanUserRepositoryMockRecorder) SetResetPasswordVerification(verification any) *MockHumanUserRepositorySetResetPasswordVerificationCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetResetPasswordVerification", reflect.TypeOf((*MockHumanUserRepository)(nil).SetResetPasswordVerification), verification) + return &MockHumanUserRepositorySetResetPasswordVerificationCall{Call: call} +} + +// MockHumanUserRepositorySetResetPasswordVerificationCall wrap *gomock.Call +type MockHumanUserRepositorySetResetPasswordVerificationCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetResetPasswordVerificationCall) Return(arg0 database.Change) *MockHumanUserRepositorySetResetPasswordVerificationCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetResetPasswordVerificationCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetResetPasswordVerificationCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetResetPasswordVerificationCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetResetPasswordVerificationCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // SetState mocks base method. func (m *MockHumanUserRepository) SetState(state domain.UserState) database.Change { m.ctrl.T.Helper() @@ -2632,40 +2936,154 @@ func (c *MockHumanUserRepositorySetStateCall) DoAndReturn(f func(domain.UserStat return c } -// SetTOTP mocks base method. -func (m *MockHumanUserRepository) SetTOTP(verification domain.VerificationType) database.Change { +// SetTOTPSecret mocks base method. +func (m *MockHumanUserRepository) SetTOTPSecret(secret []byte) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetTOTP", verification) + ret := m.ctrl.Call(m, "SetTOTPSecret", secret) ret0, _ := ret[0].(database.Change) return ret0 } -// SetTOTP indicates an expected call of SetTOTP. -func (mr *MockHumanUserRepositoryMockRecorder) SetTOTP(verification any) *MockHumanUserRepositorySetTOTPCall { +// SetTOTPSecret indicates an expected call of SetTOTPSecret. +func (mr *MockHumanUserRepositoryMockRecorder) SetTOTPSecret(secret any) *MockHumanUserRepositorySetTOTPSecretCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTOTP", reflect.TypeOf((*MockHumanUserRepository)(nil).SetTOTP), verification) - return &MockHumanUserRepositorySetTOTPCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTOTPSecret", reflect.TypeOf((*MockHumanUserRepository)(nil).SetTOTPSecret), secret) + return &MockHumanUserRepositorySetTOTPSecretCall{Call: call} } -// MockHumanUserRepositorySetTOTPCall wrap *gomock.Call -type MockHumanUserRepositorySetTOTPCall struct { +// MockHumanUserRepositorySetTOTPSecretCall wrap *gomock.Call +type MockHumanUserRepositorySetTOTPSecretCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockHumanUserRepositorySetTOTPCall) Return(arg0 database.Change) *MockHumanUserRepositorySetTOTPCall { +func (c *MockHumanUserRepositorySetTOTPSecretCall) Return(arg0 database.Change) *MockHumanUserRepositorySetTOTPSecretCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetTOTPCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetTOTPCall { +func (c *MockHumanUserRepositorySetTOTPSecretCall) Do(f func([]byte) database.Change) *MockHumanUserRepositorySetTOTPSecretCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetTOTPCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetTOTPCall { +func (c *MockHumanUserRepositorySetTOTPSecretCall) DoAndReturn(f func([]byte) database.Change) *MockHumanUserRepositorySetTOTPSecretCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetTOTPVerifiedAt mocks base method. +func (m *MockHumanUserRepository) SetTOTPVerifiedAt(verifiedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTOTPVerifiedAt", verifiedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetTOTPVerifiedAt indicates an expected call of SetTOTPVerifiedAt. +func (mr *MockHumanUserRepositoryMockRecorder) SetTOTPVerifiedAt(verifiedAt any) *MockHumanUserRepositorySetTOTPVerifiedAtCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTOTPVerifiedAt", reflect.TypeOf((*MockHumanUserRepository)(nil).SetTOTPVerifiedAt), verifiedAt) + return &MockHumanUserRepositorySetTOTPVerifiedAtCall{Call: call} +} + +// MockHumanUserRepositorySetTOTPVerifiedAtCall wrap *gomock.Call +type MockHumanUserRepositorySetTOTPVerifiedAtCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetTOTPVerifiedAtCall) Return(arg0 database.Change) *MockHumanUserRepositorySetTOTPVerifiedAtCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetTOTPVerifiedAtCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySetTOTPVerifiedAtCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetTOTPVerifiedAtCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySetTOTPVerifiedAtCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetUnverifiedEmail mocks base method. +func (m *MockHumanUserRepository) SetUnverifiedEmail(address string) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetUnverifiedEmail", address) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetUnverifiedEmail indicates an expected call of SetUnverifiedEmail. +func (mr *MockHumanUserRepositoryMockRecorder) SetUnverifiedEmail(address any) *MockHumanUserRepositorySetUnverifiedEmailCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUnverifiedEmail", reflect.TypeOf((*MockHumanUserRepository)(nil).SetUnverifiedEmail), address) + return &MockHumanUserRepositorySetUnverifiedEmailCall{Call: call} +} + +// MockHumanUserRepositorySetUnverifiedEmailCall wrap *gomock.Call +type MockHumanUserRepositorySetUnverifiedEmailCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetUnverifiedEmailCall) Return(arg0 database.Change) *MockHumanUserRepositorySetUnverifiedEmailCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetUnverifiedEmailCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetUnverifiedEmailCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetUnverifiedEmailCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetUnverifiedEmailCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetUnverifiedPhone mocks base method. +func (m *MockHumanUserRepository) SetUnverifiedPhone(number string) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetUnverifiedPhone", number) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetUnverifiedPhone indicates an expected call of SetUnverifiedPhone. +func (mr *MockHumanUserRepositoryMockRecorder) SetUnverifiedPhone(number any) *MockHumanUserRepositorySetUnverifiedPhoneCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUnverifiedPhone", reflect.TypeOf((*MockHumanUserRepository)(nil).SetUnverifiedPhone), number) + return &MockHumanUserRepositorySetUnverifiedPhoneCall{Call: call} +} + +// MockHumanUserRepositorySetUnverifiedPhoneCall wrap *gomock.Call +type MockHumanUserRepositorySetUnverifiedPhoneCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySetUnverifiedPhoneCall) Return(arg0 database.Change) *MockHumanUserRepositorySetUnverifiedPhoneCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySetUnverifiedPhoneCall) Do(f func(string) database.Change) *MockHumanUserRepositorySetUnverifiedPhoneCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySetUnverifiedPhoneCall) DoAndReturn(f func(string) database.Change) *MockHumanUserRepositorySetUnverifiedPhoneCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2747,17 +3165,17 @@ func (c *MockHumanUserRepositorySetUsernameCall) DoAndReturn(f func(string) data } // SetVerification mocks base method. -func (m *MockHumanUserRepository) SetVerification(id string, verification domain.VerificationType) database.Change { +func (m *MockHumanUserRepository) SetVerification(verification domain.VerificationType) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetVerification", id, verification) + ret := m.ctrl.Call(m, "SetVerification", verification) ret0, _ := ret[0].(database.Change) return ret0 } // SetVerification indicates an expected call of SetVerification. -func (mr *MockHumanUserRepositoryMockRecorder) SetVerification(id, verification any) *MockHumanUserRepositorySetVerificationCall { +func (mr *MockHumanUserRepositoryMockRecorder) SetVerification(verification any) *MockHumanUserRepositorySetVerificationCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVerification", reflect.TypeOf((*MockHumanUserRepository)(nil).SetVerification), id, verification) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVerification", reflect.TypeOf((*MockHumanUserRepository)(nil).SetVerification), verification) return &MockHumanUserRepositorySetVerificationCall{Call: call} } @@ -2773,13 +3191,89 @@ func (c *MockHumanUserRepositorySetVerificationCall) Return(arg0 database.Change } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositorySetVerificationCall) Do(f func(string, domain.VerificationType) database.Change) *MockHumanUserRepositorySetVerificationCall { +func (c *MockHumanUserRepositorySetVerificationCall) Do(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetVerificationCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositorySetVerificationCall) DoAndReturn(f func(string, domain.VerificationType) database.Change) *MockHumanUserRepositorySetVerificationCall { +func (c *MockHumanUserRepositorySetVerificationCall) DoAndReturn(f func(domain.VerificationType) database.Change) *MockHumanUserRepositorySetVerificationCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SkipMultifactorInitialization mocks base method. +func (m *MockHumanUserRepository) SkipMultifactorInitialization() database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SkipMultifactorInitialization") + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SkipMultifactorInitialization indicates an expected call of SkipMultifactorInitialization. +func (mr *MockHumanUserRepositoryMockRecorder) SkipMultifactorInitialization() *MockHumanUserRepositorySkipMultifactorInitializationCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SkipMultifactorInitialization", reflect.TypeOf((*MockHumanUserRepository)(nil).SkipMultifactorInitialization)) + return &MockHumanUserRepositorySkipMultifactorInitializationCall{Call: call} +} + +// MockHumanUserRepositorySkipMultifactorInitializationCall wrap *gomock.Call +type MockHumanUserRepositorySkipMultifactorInitializationCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySkipMultifactorInitializationCall) Return(arg0 database.Change) *MockHumanUserRepositorySkipMultifactorInitializationCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySkipMultifactorInitializationCall) Do(f func() database.Change) *MockHumanUserRepositorySkipMultifactorInitializationCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySkipMultifactorInitializationCall) DoAndReturn(f func() database.Change) *MockHumanUserRepositorySkipMultifactorInitializationCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SkipMultifactorInitializationAt mocks base method. +func (m *MockHumanUserRepository) SkipMultifactorInitializationAt(skippedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SkipMultifactorInitializationAt", skippedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SkipMultifactorInitializationAt indicates an expected call of SkipMultifactorInitializationAt. +func (mr *MockHumanUserRepositoryMockRecorder) SkipMultifactorInitializationAt(skippedAt any) *MockHumanUserRepositorySkipMultifactorInitializationAtCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SkipMultifactorInitializationAt", reflect.TypeOf((*MockHumanUserRepository)(nil).SkipMultifactorInitializationAt), skippedAt) + return &MockHumanUserRepositorySkipMultifactorInitializationAtCall{Call: call} +} + +// MockHumanUserRepositorySkipMultifactorInitializationAtCall wrap *gomock.Call +type MockHumanUserRepositorySkipMultifactorInitializationAtCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositorySkipMultifactorInitializationAtCall) Return(arg0 database.Change) *MockHumanUserRepositorySkipMultifactorInitializationAtCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositorySkipMultifactorInitializationAtCall) Do(f func(time.Time) database.Change) *MockHumanUserRepositorySkipMultifactorInitializationAtCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositorySkipMultifactorInitializationAtCall) DoAndReturn(f func(time.Time) database.Change) *MockHumanUserRepositorySkipMultifactorInitializationAtCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -2860,6 +3354,44 @@ func (c *MockHumanUserRepositoryStateConditionCall) DoAndReturn(f func(domain.Us return c } +// TypeColumn mocks base method. +func (m *MockHumanUserRepository) TypeColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TypeColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// TypeColumn indicates an expected call of TypeColumn. +func (mr *MockHumanUserRepositoryMockRecorder) TypeColumn() *MockHumanUserRepositoryTypeColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TypeColumn", reflect.TypeOf((*MockHumanUserRepository)(nil).TypeColumn)) + return &MockHumanUserRepositoryTypeColumnCall{Call: call} +} + +// MockHumanUserRepositoryTypeColumnCall wrap *gomock.Call +type MockHumanUserRepositoryTypeColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryTypeColumnCall) Return(arg0 database.Column) *MockHumanUserRepositoryTypeColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryTypeColumnCall) Do(f func() database.Column) *MockHumanUserRepositoryTypeColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryTypeColumnCall) DoAndReturn(f func() database.Column) *MockHumanUserRepositoryTypeColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // TypeCondition mocks base method. func (m *MockHumanUserRepository) TypeCondition(userType domain.UserType) database.Condition { m.ctrl.T.Helper() @@ -2898,6 +3430,82 @@ func (c *MockHumanUserRepositoryTypeConditionCall) DoAndReturn(f func(domain.Use return c } +// UnverifiedEmailCondition mocks base method. +func (m *MockHumanUserRepository) UnverifiedEmailCondition(op database.TextOperation, email string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnverifiedEmailCondition", op, email) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// UnverifiedEmailCondition indicates an expected call of UnverifiedEmailCondition. +func (mr *MockHumanUserRepositoryMockRecorder) UnverifiedEmailCondition(op, email any) *MockHumanUserRepositoryUnverifiedEmailConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnverifiedEmailCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).UnverifiedEmailCondition), op, email) + return &MockHumanUserRepositoryUnverifiedEmailConditionCall{Call: call} +} + +// MockHumanUserRepositoryUnverifiedEmailConditionCall wrap *gomock.Call +type MockHumanUserRepositoryUnverifiedEmailConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryUnverifiedEmailConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryUnverifiedEmailConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryUnverifiedEmailConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryUnverifiedEmailConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryUnverifiedEmailConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryUnverifiedEmailConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UnverifiedPhoneCondition mocks base method. +func (m *MockHumanUserRepository) UnverifiedPhoneCondition(op database.TextOperation, phone string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnverifiedPhoneCondition", op, phone) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// UnverifiedPhoneCondition indicates an expected call of UnverifiedPhoneCondition. +func (mr *MockHumanUserRepositoryMockRecorder) UnverifiedPhoneCondition(op, phone any) *MockHumanUserRepositoryUnverifiedPhoneConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnverifiedPhoneCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).UnverifiedPhoneCondition), op, phone) + return &MockHumanUserRepositoryUnverifiedPhoneConditionCall{Call: call} +} + +// MockHumanUserRepositoryUnverifiedPhoneConditionCall wrap *gomock.Call +type MockHumanUserRepositoryUnverifiedPhoneConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryUnverifiedPhoneConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryUnverifiedPhoneConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryUnverifiedPhoneConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryUnverifiedPhoneConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryUnverifiedPhoneConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryUnverifiedPhoneConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // Update mocks base method. func (m *MockHumanUserRepository) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { m.ctrl.T.Helper() @@ -2943,9 +3551,9 @@ func (c *MockHumanUserRepositoryUpdateCall) DoAndReturn(f func(context.Context, } // UpdateIdentityProviderLink mocks base method. -func (m *MockHumanUserRepository) UpdateIdentityProviderLink(changes ...database.Change) database.Change { +func (m *MockHumanUserRepository) UpdateIdentityProviderLink(condition database.Condition, changes ...database.Change) database.Change { m.ctrl.T.Helper() - varargs := []any{} + varargs := []any{condition} for _, a := range changes { varargs = append(varargs, a) } @@ -2955,9 +3563,10 @@ func (m *MockHumanUserRepository) UpdateIdentityProviderLink(changes ...database } // UpdateIdentityProviderLink indicates an expected call of UpdateIdentityProviderLink. -func (mr *MockHumanUserRepositoryMockRecorder) UpdateIdentityProviderLink(changes ...any) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { +func (mr *MockHumanUserRepositoryMockRecorder) UpdateIdentityProviderLink(condition any, changes ...any) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIdentityProviderLink", reflect.TypeOf((*MockHumanUserRepository)(nil).UpdateIdentityProviderLink), changes...) + varargs := append([]any{condition}, changes...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIdentityProviderLink", reflect.TypeOf((*MockHumanUserRepository)(nil).UpdateIdentityProviderLink), varargs...) return &MockHumanUserRepositoryUpdateIdentityProviderLinkCall{Call: call} } @@ -2973,13 +3582,13 @@ func (c *MockHumanUserRepositoryUpdateIdentityProviderLinkCall) Return(arg0 data } // Do rewrite *gomock.Call.Do -func (c *MockHumanUserRepositoryUpdateIdentityProviderLinkCall) Do(f func(...database.Change) database.Change) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { +func (c *MockHumanUserRepositoryUpdateIdentityProviderLinkCall) Do(f func(database.Condition, ...database.Change) database.Change) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockHumanUserRepositoryUpdateIdentityProviderLinkCall) DoAndReturn(f func(...database.Change) database.Change) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { +func (c *MockHumanUserRepositoryUpdateIdentityProviderLinkCall) DoAndReturn(f func(database.Condition, ...database.Change) database.Change) *MockHumanUserRepositoryUpdateIdentityProviderLinkCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -3102,3 +3711,79 @@ func (c *MockHumanUserRepositoryUsernameConditionCall) DoAndReturn(f func(databa c.Call = c.Call.DoAndReturn(f) return c } + +// VerifiedEmailCondition mocks base method. +func (m *MockHumanUserRepository) VerifiedEmailCondition(op database.TextOperation, email string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifiedEmailCondition", op, email) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// VerifiedEmailCondition indicates an expected call of VerifiedEmailCondition. +func (mr *MockHumanUserRepositoryMockRecorder) VerifiedEmailCondition(op, email any) *MockHumanUserRepositoryVerifiedEmailConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifiedEmailCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).VerifiedEmailCondition), op, email) + return &MockHumanUserRepositoryVerifiedEmailConditionCall{Call: call} +} + +// MockHumanUserRepositoryVerifiedEmailConditionCall wrap *gomock.Call +type MockHumanUserRepositoryVerifiedEmailConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryVerifiedEmailConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryVerifiedEmailConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryVerifiedEmailConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryVerifiedEmailConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryVerifiedEmailConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryVerifiedEmailConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// VerifiedPhoneCondition mocks base method. +func (m *MockHumanUserRepository) VerifiedPhoneCondition(op database.TextOperation, phone string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifiedPhoneCondition", op, phone) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// VerifiedPhoneCondition indicates an expected call of VerifiedPhoneCondition. +func (mr *MockHumanUserRepositoryMockRecorder) VerifiedPhoneCondition(op, phone any) *MockHumanUserRepositoryVerifiedPhoneConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifiedPhoneCondition", reflect.TypeOf((*MockHumanUserRepository)(nil).VerifiedPhoneCondition), op, phone) + return &MockHumanUserRepositoryVerifiedPhoneConditionCall{Call: call} +} + +// MockHumanUserRepositoryVerifiedPhoneConditionCall wrap *gomock.Call +type MockHumanUserRepositoryVerifiedPhoneConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockHumanUserRepositoryVerifiedPhoneConditionCall) Return(arg0 database.Condition) *MockHumanUserRepositoryVerifiedPhoneConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockHumanUserRepositoryVerifiedPhoneConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryVerifiedPhoneConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockHumanUserRepositoryVerifiedPhoneConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockHumanUserRepositoryVerifiedPhoneConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/backend/v3/domain/mock/instance_domain.mock.go b/backend/v3/domain/mock/instance_domain.mock.go index f2871832f..131a1e957 100644 --- a/backend/v3/domain/mock/instance_domain.mock.go +++ b/backend/v3/domain/mock/instance_domain.mock.go @@ -354,17 +354,17 @@ func (c *MockInstanceDomainRepositoryIsGeneratedColumnCall) DoAndReturn(f func() } // IsGeneratedCondition mocks base method. -func (m *MockInstanceDomainRepository) IsGeneratedCondition(arg0 bool) database.Condition { +func (m *MockInstanceDomainRepository) IsGeneratedCondition(isGenerated bool) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsGeneratedCondition", arg0) + ret := m.ctrl.Call(m, "IsGeneratedCondition", isGenerated) ret0, _ := ret[0].(database.Condition) return ret0 } // IsGeneratedCondition indicates an expected call of IsGeneratedCondition. -func (mr *MockInstanceDomainRepositoryMockRecorder) IsGeneratedCondition(arg0 any) *MockInstanceDomainRepositoryIsGeneratedConditionCall { +func (mr *MockInstanceDomainRepositoryMockRecorder) IsGeneratedCondition(isGenerated any) *MockInstanceDomainRepositoryIsGeneratedConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGeneratedCondition", reflect.TypeOf((*MockInstanceDomainRepository)(nil).IsGeneratedCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsGeneratedCondition", reflect.TypeOf((*MockInstanceDomainRepository)(nil).IsGeneratedCondition), isGenerated) return &MockInstanceDomainRepositoryIsGeneratedConditionCall{Call: call} } diff --git a/backend/v3/domain/mock/machine_user.mock.go b/backend/v3/domain/mock/machine_user.mock.go index 9d3e7e40a..2019b9776 100644 --- a/backend/v3/domain/mock/machine_user.mock.go +++ b/backend/v3/domain/mock/machine_user.mock.go @@ -237,6 +237,44 @@ func (c *MockMachineUserRepositoryExistsMetadataCall) DoAndReturn(f func(databas return c } +// IDColumn mocks base method. +func (m *MockMachineUserRepository) IDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// IDColumn indicates an expected call of IDColumn. +func (mr *MockMachineUserRepositoryMockRecorder) IDColumn() *MockMachineUserRepositoryIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDColumn", reflect.TypeOf((*MockMachineUserRepository)(nil).IDColumn)) + return &MockMachineUserRepositoryIDColumnCall{Call: call} +} + +// MockMachineUserRepositoryIDColumnCall wrap *gomock.Call +type MockMachineUserRepositoryIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockMachineUserRepositoryIDColumnCall) Return(arg0 database.Column) *MockMachineUserRepositoryIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockMachineUserRepositoryIDColumnCall) Do(f func() database.Column) *MockMachineUserRepositoryIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockMachineUserRepositoryIDColumnCall) DoAndReturn(f func() database.Column) *MockMachineUserRepositoryIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // IDCondition mocks base method. func (m *MockMachineUserRepository) IDCondition(userID string) database.Condition { m.ctrl.T.Helper() @@ -275,6 +313,44 @@ func (c *MockMachineUserRepositoryIDConditionCall) DoAndReturn(f func(string) da return c } +// InstanceIDColumn mocks base method. +func (m *MockMachineUserRepository) InstanceIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// InstanceIDColumn indicates an expected call of InstanceIDColumn. +func (mr *MockMachineUserRepositoryMockRecorder) InstanceIDColumn() *MockMachineUserRepositoryInstanceIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDColumn", reflect.TypeOf((*MockMachineUserRepository)(nil).InstanceIDColumn)) + return &MockMachineUserRepositoryInstanceIDColumnCall{Call: call} +} + +// MockMachineUserRepositoryInstanceIDColumnCall wrap *gomock.Call +type MockMachineUserRepositoryInstanceIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockMachineUserRepositoryInstanceIDColumnCall) Return(arg0 database.Column) *MockMachineUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockMachineUserRepositoryInstanceIDColumnCall) Do(f func() database.Column) *MockMachineUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockMachineUserRepositoryInstanceIDColumnCall) DoAndReturn(f func() database.Column) *MockMachineUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // InstanceIDCondition mocks base method. func (m *MockMachineUserRepository) InstanceIDCondition(instanceID string) database.Condition { m.ctrl.T.Helper() @@ -351,116 +427,78 @@ func (c *MockMachineUserRepositoryLoginNameConditionCall) DoAndReturn(f func(dat return c } -// MetadataKeyCondition mocks base method. -func (m *MockMachineUserRepository) MetadataKeyCondition(key string) database.Condition { +// MetadataConditions mocks base method. +func (m *MockMachineUserRepository) MetadataConditions() domain.UserMetadataConditions { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataKeyCondition", key) - ret0, _ := ret[0].(database.Condition) + ret := m.ctrl.Call(m, "MetadataConditions") + ret0, _ := ret[0].(domain.UserMetadataConditions) return ret0 } -// MetadataKeyCondition indicates an expected call of MetadataKeyCondition. -func (mr *MockMachineUserRepositoryMockRecorder) MetadataKeyCondition(key any) *MockMachineUserRepositoryMetadataKeyConditionCall { +// MetadataConditions indicates an expected call of MetadataConditions. +func (mr *MockMachineUserRepositoryMockRecorder) MetadataConditions() *MockMachineUserRepositoryMetadataConditionsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataKeyCondition", reflect.TypeOf((*MockMachineUserRepository)(nil).MetadataKeyCondition), key) - return &MockMachineUserRepositoryMetadataKeyConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataConditions", reflect.TypeOf((*MockMachineUserRepository)(nil).MetadataConditions)) + return &MockMachineUserRepositoryMetadataConditionsCall{Call: call} } -// MockMachineUserRepositoryMetadataKeyConditionCall wrap *gomock.Call -type MockMachineUserRepositoryMetadataKeyConditionCall struct { +// MockMachineUserRepositoryMetadataConditionsCall wrap *gomock.Call +type MockMachineUserRepositoryMetadataConditionsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockMachineUserRepositoryMetadataKeyConditionCall) Return(arg0 database.Condition) *MockMachineUserRepositoryMetadataKeyConditionCall { +func (c *MockMachineUserRepositoryMetadataConditionsCall) Return(arg0 domain.UserMetadataConditions) *MockMachineUserRepositoryMetadataConditionsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockMachineUserRepositoryMetadataKeyConditionCall) Do(f func(string) database.Condition) *MockMachineUserRepositoryMetadataKeyConditionCall { +func (c *MockMachineUserRepositoryMetadataConditionsCall) Do(f func() domain.UserMetadataConditions) *MockMachineUserRepositoryMetadataConditionsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockMachineUserRepositoryMetadataKeyConditionCall) DoAndReturn(f func(string) database.Condition) *MockMachineUserRepositoryMetadataKeyConditionCall { +func (c *MockMachineUserRepositoryMetadataConditionsCall) DoAndReturn(f func() domain.UserMetadataConditions) *MockMachineUserRepositoryMetadataConditionsCall { c.Call = c.Call.DoAndReturn(f) return c } -// MetadataValueCondition mocks base method. -func (m *MockMachineUserRepository) MetadataValueCondition(op database.BytesOperation, value []byte) database.Condition { +// OrganizationIDCondition mocks base method. +func (m *MockMachineUserRepository) OrganizationIDCondition(orgID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataValueCondition", op, value) + ret := m.ctrl.Call(m, "OrganizationIDCondition", orgID) ret0, _ := ret[0].(database.Condition) return ret0 } -// MetadataValueCondition indicates an expected call of MetadataValueCondition. -func (mr *MockMachineUserRepositoryMockRecorder) MetadataValueCondition(op, value any) *MockMachineUserRepositoryMetadataValueConditionCall { +// OrganizationIDCondition indicates an expected call of OrganizationIDCondition. +func (mr *MockMachineUserRepositoryMockRecorder) OrganizationIDCondition(orgID any) *MockMachineUserRepositoryOrganizationIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataValueCondition", reflect.TypeOf((*MockMachineUserRepository)(nil).MetadataValueCondition), op, value) - return &MockMachineUserRepositoryMetadataValueConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrganizationIDCondition", reflect.TypeOf((*MockMachineUserRepository)(nil).OrganizationIDCondition), orgID) + return &MockMachineUserRepositoryOrganizationIDConditionCall{Call: call} } -// MockMachineUserRepositoryMetadataValueConditionCall wrap *gomock.Call -type MockMachineUserRepositoryMetadataValueConditionCall struct { +// MockMachineUserRepositoryOrganizationIDConditionCall wrap *gomock.Call +type MockMachineUserRepositoryOrganizationIDConditionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockMachineUserRepositoryMetadataValueConditionCall) Return(arg0 database.Condition) *MockMachineUserRepositoryMetadataValueConditionCall { +func (c *MockMachineUserRepositoryOrganizationIDConditionCall) Return(arg0 database.Condition) *MockMachineUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockMachineUserRepositoryMetadataValueConditionCall) Do(f func(database.BytesOperation, []byte) database.Condition) *MockMachineUserRepositoryMetadataValueConditionCall { +func (c *MockMachineUserRepositoryOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockMachineUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockMachineUserRepositoryMetadataValueConditionCall) DoAndReturn(f func(database.BytesOperation, []byte) database.Condition) *MockMachineUserRepositoryMetadataValueConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// OrgIDCondition mocks base method. -func (m *MockMachineUserRepository) OrgIDCondition(orgID string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OrgIDCondition", orgID) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// OrgIDCondition indicates an expected call of OrgIDCondition. -func (mr *MockMachineUserRepositoryMockRecorder) OrgIDCondition(orgID any) *MockMachineUserRepositoryOrgIDConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrgIDCondition", reflect.TypeOf((*MockMachineUserRepository)(nil).OrgIDCondition), orgID) - return &MockMachineUserRepositoryOrgIDConditionCall{Call: call} -} - -// MockMachineUserRepositoryOrgIDConditionCall wrap *gomock.Call -type MockMachineUserRepositoryOrgIDConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockMachineUserRepositoryOrgIDConditionCall) Return(arg0 database.Condition) *MockMachineUserRepositoryOrgIDConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockMachineUserRepositoryOrgIDConditionCall) Do(f func(string) database.Condition) *MockMachineUserRepositoryOrgIDConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockMachineUserRepositoryOrgIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockMachineUserRepositoryOrgIDConditionCall { +func (c *MockMachineUserRepositoryOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockMachineUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -656,7 +694,7 @@ func (c *MockMachineUserRepositoryRemovePersonalAccessTokenCall) DoAndReturn(f f } // SetAccessTokenType mocks base method. -func (m *MockMachineUserRepository) SetAccessTokenType(tokenType domain.PersonalAccessTokenType) database.Change { +func (m *MockMachineUserRepository) SetAccessTokenType(tokenType domain.AccessTokenType) database.Change { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetAccessTokenType", tokenType) ret0, _ := ret[0].(database.Change) @@ -682,13 +720,13 @@ func (c *MockMachineUserRepositorySetAccessTokenTypeCall) Return(arg0 database.C } // Do rewrite *gomock.Call.Do -func (c *MockMachineUserRepositorySetAccessTokenTypeCall) Do(f func(domain.PersonalAccessTokenType) database.Change) *MockMachineUserRepositorySetAccessTokenTypeCall { +func (c *MockMachineUserRepositorySetAccessTokenTypeCall) Do(f func(domain.AccessTokenType) database.Change) *MockMachineUserRepositorySetAccessTokenTypeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockMachineUserRepositorySetAccessTokenTypeCall) DoAndReturn(f func(domain.PersonalAccessTokenType) database.Change) *MockMachineUserRepositorySetAccessTokenTypeCall { +func (c *MockMachineUserRepositorySetAccessTokenTypeCall) DoAndReturn(f func(domain.AccessTokenType) database.Change) *MockMachineUserRepositorySetAccessTokenTypeCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -997,6 +1035,44 @@ func (c *MockMachineUserRepositoryStateConditionCall) DoAndReturn(f func(domain. return c } +// TypeColumn mocks base method. +func (m *MockMachineUserRepository) TypeColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TypeColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// TypeColumn indicates an expected call of TypeColumn. +func (mr *MockMachineUserRepositoryMockRecorder) TypeColumn() *MockMachineUserRepositoryTypeColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TypeColumn", reflect.TypeOf((*MockMachineUserRepository)(nil).TypeColumn)) + return &MockMachineUserRepositoryTypeColumnCall{Call: call} +} + +// MockMachineUserRepositoryTypeColumnCall wrap *gomock.Call +type MockMachineUserRepositoryTypeColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockMachineUserRepositoryTypeColumnCall) Return(arg0 database.Column) *MockMachineUserRepositoryTypeColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockMachineUserRepositoryTypeColumnCall) Do(f func() database.Column) *MockMachineUserRepositoryTypeColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockMachineUserRepositoryTypeColumnCall) DoAndReturn(f func() database.Column) *MockMachineUserRepositoryTypeColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // TypeCondition mocks base method. func (m *MockMachineUserRepository) TypeCondition(userType domain.UserType) database.Condition { m.ctrl.T.Helper() diff --git a/backend/v3/domain/mock/project.mock.go b/backend/v3/domain/mock/project.mock.go index 9fcb2d4db..f2ae63580 100644 --- a/backend/v3/domain/mock/project.mock.go +++ b/backend/v3/domain/mock/project.mock.go @@ -202,6 +202,82 @@ func (c *MockProjectRepositoryGetCall) DoAndReturn(f func(context.Context, datab return c } +// GrantedOrganizationIDColumn mocks base method. +func (m *MockProjectRepository) GrantedOrganizationIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantedOrganizationIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// GrantedOrganizationIDColumn indicates an expected call of GrantedOrganizationIDColumn. +func (mr *MockProjectRepositoryMockRecorder) GrantedOrganizationIDColumn() *MockProjectRepositoryGrantedOrganizationIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantedOrganizationIDColumn", reflect.TypeOf((*MockProjectRepository)(nil).GrantedOrganizationIDColumn)) + return &MockProjectRepositoryGrantedOrganizationIDColumnCall{Call: call} +} + +// MockProjectRepositoryGrantedOrganizationIDColumnCall wrap *gomock.Call +type MockProjectRepositoryGrantedOrganizationIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectRepositoryGrantedOrganizationIDColumnCall) Return(arg0 database.Column) *MockProjectRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectRepositoryGrantedOrganizationIDColumnCall) Do(f func() database.Column) *MockProjectRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectRepositoryGrantedOrganizationIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GrantedOrganizationIDCondition mocks base method. +func (m *MockProjectRepository) GrantedOrganizationIDCondition(organizationID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantedOrganizationIDCondition", organizationID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// GrantedOrganizationIDCondition indicates an expected call of GrantedOrganizationIDCondition. +func (mr *MockProjectRepositoryMockRecorder) GrantedOrganizationIDCondition(organizationID any) *MockProjectRepositoryGrantedOrganizationIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantedOrganizationIDCondition", reflect.TypeOf((*MockProjectRepository)(nil).GrantedOrganizationIDCondition), organizationID) + return &MockProjectRepositoryGrantedOrganizationIDConditionCall{Call: call} +} + +// MockProjectRepositoryGrantedOrganizationIDConditionCall wrap *gomock.Call +type MockProjectRepositoryGrantedOrganizationIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectRepositoryGrantedOrganizationIDConditionCall) Return(arg0 database.Condition) *MockProjectRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectRepositoryGrantedOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockProjectRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectRepositoryGrantedOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // IDColumn mocks base method. func (m *MockProjectRepository) IDColumn() database.Column { m.ctrl.T.Helper() diff --git a/backend/v3/domain/mock/project_grant.mock.go b/backend/v3/domain/mock/project_grant.mock.go new file mode 100644 index 000000000..88a8f3454 --- /dev/null +++ b/backend/v3/domain/mock/project_grant.mock.go @@ -0,0 +1,1013 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/zitadel/zitadel/backend/v3/domain (interfaces: ProjectGrantRepository) +// +// Generated by this command: +// +// mockgen -typed -package domainmock -destination ./mock/project_grant.mock.go . ProjectGrantRepository +// + +// Package domainmock is a generated GoMock package. +package domainmock + +import ( + context "context" + reflect "reflect" + time "time" + + domain "github.com/zitadel/zitadel/backend/v3/domain" + database "github.com/zitadel/zitadel/backend/v3/storage/database" + gomock "go.uber.org/mock/gomock" +) + +// MockProjectGrantRepository is a mock of ProjectGrantRepository interface. +type MockProjectGrantRepository struct { + ctrl *gomock.Controller + recorder *MockProjectGrantRepositoryMockRecorder + isgomock struct{} +} + +// MockProjectGrantRepositoryMockRecorder is the mock recorder for MockProjectGrantRepository. +type MockProjectGrantRepositoryMockRecorder struct { + mock *MockProjectGrantRepository +} + +// NewMockProjectGrantRepository creates a new mock instance. +func NewMockProjectGrantRepository(ctrl *gomock.Controller) *MockProjectGrantRepository { + mock := &MockProjectGrantRepository{ctrl: ctrl} + mock.recorder = &MockProjectGrantRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockProjectGrantRepository) EXPECT() *MockProjectGrantRepositoryMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockProjectGrantRepository) Create(ctx context.Context, client database.QueryExecutor, project *domain.ProjectGrant) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, client, project) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockProjectGrantRepositoryMockRecorder) Create(ctx, client, project any) *MockProjectGrantRepositoryCreateCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockProjectGrantRepository)(nil).Create), ctx, client, project) + return &MockProjectGrantRepositoryCreateCall{Call: call} +} + +// MockProjectGrantRepositoryCreateCall wrap *gomock.Call +type MockProjectGrantRepositoryCreateCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryCreateCall) Return(arg0 error) *MockProjectGrantRepositoryCreateCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryCreateCall) Do(f func(context.Context, database.QueryExecutor, *domain.ProjectGrant) error) *MockProjectGrantRepositoryCreateCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryCreateCall) DoAndReturn(f func(context.Context, database.QueryExecutor, *domain.ProjectGrant) error) *MockProjectGrantRepositoryCreateCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreatedAtColumn mocks base method. +func (m *MockProjectGrantRepository) CreatedAtColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreatedAtColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// CreatedAtColumn indicates an expected call of CreatedAtColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) CreatedAtColumn() *MockProjectGrantRepositoryCreatedAtColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatedAtColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).CreatedAtColumn)) + return &MockProjectGrantRepositoryCreatedAtColumnCall{Call: call} +} + +// MockProjectGrantRepositoryCreatedAtColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryCreatedAtColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryCreatedAtColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryCreatedAtColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryCreatedAtColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryCreatedAtColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryCreatedAtColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryCreatedAtColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Delete mocks base method. +func (m *MockProjectGrantRepository) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, client, condition) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockProjectGrantRepositoryMockRecorder) Delete(ctx, client, condition any) *MockProjectGrantRepositoryDeleteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProjectGrantRepository)(nil).Delete), ctx, client, condition) + return &MockProjectGrantRepositoryDeleteCall{Call: call} +} + +// MockProjectGrantRepositoryDeleteCall wrap *gomock.Call +type MockProjectGrantRepositoryDeleteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryDeleteCall) Return(arg0 int64, arg1 error) *MockProjectGrantRepositoryDeleteCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryDeleteCall) Do(f func(context.Context, database.QueryExecutor, database.Condition) (int64, error)) *MockProjectGrantRepositoryDeleteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryDeleteCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition) (int64, error)) *MockProjectGrantRepositoryDeleteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ExistsRoleKey mocks base method. +func (m *MockProjectGrantRepository) ExistsRoleKey(cond database.Condition) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExistsRoleKey", cond) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ExistsRoleKey indicates an expected call of ExistsRoleKey. +func (mr *MockProjectGrantRepositoryMockRecorder) ExistsRoleKey(cond any) *MockProjectGrantRepositoryExistsRoleKeyCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsRoleKey", reflect.TypeOf((*MockProjectGrantRepository)(nil).ExistsRoleKey), cond) + return &MockProjectGrantRepositoryExistsRoleKeyCall{Call: call} +} + +// MockProjectGrantRepositoryExistsRoleKeyCall wrap *gomock.Call +type MockProjectGrantRepositoryExistsRoleKeyCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryExistsRoleKeyCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryExistsRoleKeyCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryExistsRoleKeyCall) Do(f func(database.Condition) database.Condition) *MockProjectGrantRepositoryExistsRoleKeyCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryExistsRoleKeyCall) DoAndReturn(f func(database.Condition) database.Condition) *MockProjectGrantRepositoryExistsRoleKeyCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Get mocks base method. +func (m *MockProjectGrantRepository) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.ProjectGrant, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(*domain.ProjectGrant) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockProjectGrantRepositoryMockRecorder) Get(ctx, client any, opts ...any) *MockProjectGrantRepositoryGetCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client}, opts...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockProjectGrantRepository)(nil).Get), varargs...) + return &MockProjectGrantRepositoryGetCall{Call: call} +} + +// MockProjectGrantRepositoryGetCall wrap *gomock.Call +type MockProjectGrantRepositoryGetCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryGetCall) Return(arg0 *domain.ProjectGrant, arg1 error) *MockProjectGrantRepositoryGetCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryGetCall) Do(f func(context.Context, database.QueryExecutor, ...database.QueryOption) (*domain.ProjectGrant, error)) *MockProjectGrantRepositoryGetCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryGetCall) DoAndReturn(f func(context.Context, database.QueryExecutor, ...database.QueryOption) (*domain.ProjectGrant, error)) *MockProjectGrantRepositoryGetCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GrantedOrganizationIDColumn mocks base method. +func (m *MockProjectGrantRepository) GrantedOrganizationIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantedOrganizationIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// GrantedOrganizationIDColumn indicates an expected call of GrantedOrganizationIDColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) GrantedOrganizationIDColumn() *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantedOrganizationIDColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).GrantedOrganizationIDColumn)) + return &MockProjectGrantRepositoryGrantedOrganizationIDColumnCall{Call: call} +} + +// MockProjectGrantRepositoryGrantedOrganizationIDColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryGrantedOrganizationIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryGrantedOrganizationIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GrantedOrganizationIDCondition mocks base method. +func (m *MockProjectGrantRepository) GrantedOrganizationIDCondition(grantedOrgID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantedOrganizationIDCondition", grantedOrgID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// GrantedOrganizationIDCondition indicates an expected call of GrantedOrganizationIDCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) GrantedOrganizationIDCondition(grantedOrgID any) *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantedOrganizationIDCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).GrantedOrganizationIDCondition), grantedOrgID) + return &MockProjectGrantRepositoryGrantedOrganizationIDConditionCall{Call: call} +} + +// MockProjectGrantRepositoryGrantedOrganizationIDConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryGrantedOrganizationIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectGrantRepositoryGrantedOrganizationIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GrantingOrganizationIDColumn mocks base method. +func (m *MockProjectGrantRepository) GrantingOrganizationIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantingOrganizationIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// GrantingOrganizationIDColumn indicates an expected call of GrantingOrganizationIDColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) GrantingOrganizationIDColumn() *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantingOrganizationIDColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).GrantingOrganizationIDColumn)) + return &MockProjectGrantRepositoryGrantingOrganizationIDColumnCall{Call: call} +} + +// MockProjectGrantRepositoryGrantingOrganizationIDColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryGrantingOrganizationIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryGrantingOrganizationIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GrantingOrganizationIDCondition mocks base method. +func (m *MockProjectGrantRepository) GrantingOrganizationIDCondition(grantingOrgID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantingOrganizationIDCondition", grantingOrgID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// GrantingOrganizationIDCondition indicates an expected call of GrantingOrganizationIDCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) GrantingOrganizationIDCondition(grantingOrgID any) *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantingOrganizationIDCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).GrantingOrganizationIDCondition), grantingOrgID) + return &MockProjectGrantRepositoryGrantingOrganizationIDConditionCall{Call: call} +} + +// MockProjectGrantRepositoryGrantingOrganizationIDConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryGrantingOrganizationIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectGrantRepositoryGrantingOrganizationIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IDColumn mocks base method. +func (m *MockProjectGrantRepository) IDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// IDColumn indicates an expected call of IDColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) IDColumn() *MockProjectGrantRepositoryIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).IDColumn)) + return &MockProjectGrantRepositoryIDColumnCall{Call: call} +} + +// MockProjectGrantRepositoryIDColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryIDColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryIDColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IDCondition mocks base method. +func (m *MockProjectGrantRepository) IDCondition(id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IDCondition", id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// IDCondition indicates an expected call of IDCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) IDCondition(id any) *MockProjectGrantRepositoryIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).IDCondition), id) + return &MockProjectGrantRepositoryIDConditionCall{Call: call} +} + +// MockProjectGrantRepositoryIDConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryIDConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryIDConditionCall) Do(f func(string) database.Condition) *MockProjectGrantRepositoryIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectGrantRepositoryIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// InstanceIDColumn mocks base method. +func (m *MockProjectGrantRepository) InstanceIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// InstanceIDColumn indicates an expected call of InstanceIDColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) InstanceIDColumn() *MockProjectGrantRepositoryInstanceIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).InstanceIDColumn)) + return &MockProjectGrantRepositoryInstanceIDColumnCall{Call: call} +} + +// MockProjectGrantRepositoryInstanceIDColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryInstanceIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryInstanceIDColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryInstanceIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryInstanceIDColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryInstanceIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryInstanceIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryInstanceIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// InstanceIDCondition mocks base method. +func (m *MockProjectGrantRepository) InstanceIDCondition(instanceID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDCondition", instanceID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// InstanceIDCondition indicates an expected call of InstanceIDCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) InstanceIDCondition(instanceID any) *MockProjectGrantRepositoryInstanceIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).InstanceIDCondition), instanceID) + return &MockProjectGrantRepositoryInstanceIDConditionCall{Call: call} +} + +// MockProjectGrantRepositoryInstanceIDConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryInstanceIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryInstanceIDConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryInstanceIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryInstanceIDConditionCall) Do(f func(string) database.Condition) *MockProjectGrantRepositoryInstanceIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryInstanceIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectGrantRepositoryInstanceIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// List mocks base method. +func (m *MockProjectGrantRepository) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.ProjectGrant, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].([]*domain.ProjectGrant) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockProjectGrantRepositoryMockRecorder) List(ctx, client any, opts ...any) *MockProjectGrantRepositoryListCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client}, opts...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockProjectGrantRepository)(nil).List), varargs...) + return &MockProjectGrantRepositoryListCall{Call: call} +} + +// MockProjectGrantRepositoryListCall wrap *gomock.Call +type MockProjectGrantRepositoryListCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryListCall) Return(arg0 []*domain.ProjectGrant, arg1 error) *MockProjectGrantRepositoryListCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryListCall) Do(f func(context.Context, database.QueryExecutor, ...database.QueryOption) ([]*domain.ProjectGrant, error)) *MockProjectGrantRepositoryListCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryListCall) DoAndReturn(f func(context.Context, database.QueryExecutor, ...database.QueryOption) ([]*domain.ProjectGrant, error)) *MockProjectGrantRepositoryListCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// PrimaryKeyColumns mocks base method. +func (m *MockProjectGrantRepository) PrimaryKeyColumns() []database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrimaryKeyColumns") + ret0, _ := ret[0].([]database.Column) + return ret0 +} + +// PrimaryKeyColumns indicates an expected call of PrimaryKeyColumns. +func (mr *MockProjectGrantRepositoryMockRecorder) PrimaryKeyColumns() *MockProjectGrantRepositoryPrimaryKeyColumnsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyColumns", reflect.TypeOf((*MockProjectGrantRepository)(nil).PrimaryKeyColumns)) + return &MockProjectGrantRepositoryPrimaryKeyColumnsCall{Call: call} +} + +// MockProjectGrantRepositoryPrimaryKeyColumnsCall wrap *gomock.Call +type MockProjectGrantRepositoryPrimaryKeyColumnsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryPrimaryKeyColumnsCall) Return(arg0 []database.Column) *MockProjectGrantRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryPrimaryKeyColumnsCall) Do(f func() []database.Column) *MockProjectGrantRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryPrimaryKeyColumnsCall) DoAndReturn(f func() []database.Column) *MockProjectGrantRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// PrimaryKeyCondition mocks base method. +func (m *MockProjectGrantRepository) PrimaryKeyCondition(instanceID, id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrimaryKeyCondition", instanceID, id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// PrimaryKeyCondition indicates an expected call of PrimaryKeyCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) PrimaryKeyCondition(instanceID, id any) *MockProjectGrantRepositoryPrimaryKeyConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).PrimaryKeyCondition), instanceID, id) + return &MockProjectGrantRepositoryPrimaryKeyConditionCall{Call: call} +} + +// MockProjectGrantRepositoryPrimaryKeyConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryPrimaryKeyConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryPrimaryKeyConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryPrimaryKeyConditionCall) Do(f func(string, string) database.Condition) *MockProjectGrantRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryPrimaryKeyConditionCall) DoAndReturn(f func(string, string) database.Condition) *MockProjectGrantRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProjectIDColumn mocks base method. +func (m *MockProjectGrantRepository) ProjectIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// ProjectIDColumn indicates an expected call of ProjectIDColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) ProjectIDColumn() *MockProjectGrantRepositoryProjectIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectIDColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).ProjectIDColumn)) + return &MockProjectGrantRepositoryProjectIDColumnCall{Call: call} +} + +// MockProjectGrantRepositoryProjectIDColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryProjectIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryProjectIDColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryProjectIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryProjectIDColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryProjectIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryProjectIDColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryProjectIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProjectIDCondition mocks base method. +func (m *MockProjectGrantRepository) ProjectIDCondition(projectID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectIDCondition", projectID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ProjectIDCondition indicates an expected call of ProjectIDCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) ProjectIDCondition(projectID any) *MockProjectGrantRepositoryProjectIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectIDCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).ProjectIDCondition), projectID) + return &MockProjectGrantRepositoryProjectIDConditionCall{Call: call} +} + +// MockProjectGrantRepositoryProjectIDConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryProjectIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryProjectIDConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryProjectIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryProjectIDConditionCall) Do(f func(string) database.Condition) *MockProjectGrantRepositoryProjectIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryProjectIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockProjectGrantRepositoryProjectIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// RoleKeyCondition mocks base method. +func (m *MockProjectGrantRepository) RoleKeyCondition(op database.TextOperation, role string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RoleKeyCondition", op, role) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// RoleKeyCondition indicates an expected call of RoleKeyCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) RoleKeyCondition(op, role any) *MockProjectGrantRepositoryRoleKeyConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoleKeyCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).RoleKeyCondition), op, role) + return &MockProjectGrantRepositoryRoleKeyConditionCall{Call: call} +} + +// MockProjectGrantRepositoryRoleKeyConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryRoleKeyConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryRoleKeyConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryRoleKeyConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryRoleKeyConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockProjectGrantRepositoryRoleKeyConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryRoleKeyConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockProjectGrantRepositoryRoleKeyConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetState mocks base method. +func (m *MockProjectGrantRepository) SetState(state domain.ProjectGrantState) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetState", state) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetState indicates an expected call of SetState. +func (mr *MockProjectGrantRepositoryMockRecorder) SetState(state any) *MockProjectGrantRepositorySetStateCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetState", reflect.TypeOf((*MockProjectGrantRepository)(nil).SetState), state) + return &MockProjectGrantRepositorySetStateCall{Call: call} +} + +// MockProjectGrantRepositorySetStateCall wrap *gomock.Call +type MockProjectGrantRepositorySetStateCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositorySetStateCall) Return(arg0 database.Change) *MockProjectGrantRepositorySetStateCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositorySetStateCall) Do(f func(domain.ProjectGrantState) database.Change) *MockProjectGrantRepositorySetStateCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositorySetStateCall) DoAndReturn(f func(domain.ProjectGrantState) database.Change) *MockProjectGrantRepositorySetStateCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetUpdatedAt mocks base method. +func (m *MockProjectGrantRepository) SetUpdatedAt(updatedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetUpdatedAt", updatedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetUpdatedAt indicates an expected call of SetUpdatedAt. +func (mr *MockProjectGrantRepositoryMockRecorder) SetUpdatedAt(updatedAt any) *MockProjectGrantRepositorySetUpdatedAtCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdatedAt", reflect.TypeOf((*MockProjectGrantRepository)(nil).SetUpdatedAt), updatedAt) + return &MockProjectGrantRepositorySetUpdatedAtCall{Call: call} +} + +// MockProjectGrantRepositorySetUpdatedAtCall wrap *gomock.Call +type MockProjectGrantRepositorySetUpdatedAtCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositorySetUpdatedAtCall) Return(arg0 database.Change) *MockProjectGrantRepositorySetUpdatedAtCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositorySetUpdatedAtCall) Do(f func(time.Time) database.Change) *MockProjectGrantRepositorySetUpdatedAtCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositorySetUpdatedAtCall) DoAndReturn(f func(time.Time) database.Change) *MockProjectGrantRepositorySetUpdatedAtCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// StateColumn mocks base method. +func (m *MockProjectGrantRepository) StateColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// StateColumn indicates an expected call of StateColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) StateColumn() *MockProjectGrantRepositoryStateColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).StateColumn)) + return &MockProjectGrantRepositoryStateColumnCall{Call: call} +} + +// MockProjectGrantRepositoryStateColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryStateColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryStateColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryStateColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryStateColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryStateColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryStateColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryStateColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// StateCondition mocks base method. +func (m *MockProjectGrantRepository) StateCondition(state domain.ProjectGrantState) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateCondition", state) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// StateCondition indicates an expected call of StateCondition. +func (mr *MockProjectGrantRepositoryMockRecorder) StateCondition(state any) *MockProjectGrantRepositoryStateConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateCondition", reflect.TypeOf((*MockProjectGrantRepository)(nil).StateCondition), state) + return &MockProjectGrantRepositoryStateConditionCall{Call: call} +} + +// MockProjectGrantRepositoryStateConditionCall wrap *gomock.Call +type MockProjectGrantRepositoryStateConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryStateConditionCall) Return(arg0 database.Condition) *MockProjectGrantRepositoryStateConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryStateConditionCall) Do(f func(domain.ProjectGrantState) database.Condition) *MockProjectGrantRepositoryStateConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryStateConditionCall) DoAndReturn(f func(domain.ProjectGrantState) database.Condition) *MockProjectGrantRepositoryStateConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Update mocks base method. +func (m *MockProjectGrantRepository) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, roleKeys []string, changes ...database.Change) (int64, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client, condition, roleKeys} + for _, a := range changes { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockProjectGrantRepositoryMockRecorder) Update(ctx, client, condition, roleKeys any, changes ...any) *MockProjectGrantRepositoryUpdateCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client, condition, roleKeys}, changes...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProjectGrantRepository)(nil).Update), varargs...) + return &MockProjectGrantRepositoryUpdateCall{Call: call} +} + +// MockProjectGrantRepositoryUpdateCall wrap *gomock.Call +type MockProjectGrantRepositoryUpdateCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryUpdateCall) Return(arg0 int64, arg1 error) *MockProjectGrantRepositoryUpdateCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryUpdateCall) Do(f func(context.Context, database.QueryExecutor, database.Condition, []string, ...database.Change) (int64, error)) *MockProjectGrantRepositoryUpdateCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryUpdateCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition, []string, ...database.Change) (int64, error)) *MockProjectGrantRepositoryUpdateCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdatedAtColumn mocks base method. +func (m *MockProjectGrantRepository) UpdatedAtColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdatedAtColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// UpdatedAtColumn indicates an expected call of UpdatedAtColumn. +func (mr *MockProjectGrantRepositoryMockRecorder) UpdatedAtColumn() *MockProjectGrantRepositoryUpdatedAtColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedAtColumn", reflect.TypeOf((*MockProjectGrantRepository)(nil).UpdatedAtColumn)) + return &MockProjectGrantRepositoryUpdatedAtColumnCall{Call: call} +} + +// MockProjectGrantRepositoryUpdatedAtColumnCall wrap *gomock.Call +type MockProjectGrantRepositoryUpdatedAtColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockProjectGrantRepositoryUpdatedAtColumnCall) Return(arg0 database.Column) *MockProjectGrantRepositoryUpdatedAtColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockProjectGrantRepositoryUpdatedAtColumnCall) Do(f func() database.Column) *MockProjectGrantRepositoryUpdatedAtColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockProjectGrantRepositoryUpdatedAtColumnCall) DoAndReturn(f func() database.Column) *MockProjectGrantRepositoryUpdatedAtColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/backend/v3/domain/mock/session.mock.go b/backend/v3/domain/mock/session.mock.go index 78fbccc75..27d56dd91 100644 --- a/backend/v3/domain/mock/session.mock.go +++ b/backend/v3/domain/mock/session.mock.go @@ -23,6 +23,7 @@ import ( type MockSessionRepository struct { ctrl *gomock.Controller recorder *MockSessionRepositoryMockRecorder + isgomock struct{} } // MockSessionRepositoryMockRecorder is the mock recorder for MockSessionRepository. @@ -43,17 +44,17 @@ func (m *MockSessionRepository) EXPECT() *MockSessionRepositoryMockRecorder { } // ClearFactor mocks base method. -func (m *MockSessionRepository) ClearFactor(arg0 domain.SessionFactorType) database.Change { +func (m *MockSessionRepository) ClearFactor(factor domain.SessionFactorType) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClearFactor", arg0) + ret := m.ctrl.Call(m, "ClearFactor", factor) ret0, _ := ret[0].(database.Change) return ret0 } // ClearFactor indicates an expected call of ClearFactor. -func (mr *MockSessionRepositoryMockRecorder) ClearFactor(arg0 any) *MockSessionRepositoryClearFactorCall { +func (mr *MockSessionRepositoryMockRecorder) ClearFactor(factor any) *MockSessionRepositoryClearFactorCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFactor", reflect.TypeOf((*MockSessionRepository)(nil).ClearFactor), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFactor", reflect.TypeOf((*MockSessionRepository)(nil).ClearFactor), factor) return &MockSessionRepositoryClearFactorCall{Call: call} } @@ -81,17 +82,17 @@ func (c *MockSessionRepositoryClearFactorCall) DoAndReturn(f func(domain.Session } // Create mocks base method. -func (m *MockSessionRepository) Create(arg0 context.Context, arg1 database.QueryExecutor, arg2 *domain.Session) error { +func (m *MockSessionRepository) Create(ctx context.Context, client database.QueryExecutor, user *domain.Session) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "Create", ctx, client, user) ret0, _ := ret[0].(error) return ret0 } // Create indicates an expected call of Create. -func (mr *MockSessionRepositoryMockRecorder) Create(arg0, arg1, arg2 any) *MockSessionRepositoryCreateCall { +func (mr *MockSessionRepositoryMockRecorder) Create(ctx, client, user any) *MockSessionRepositoryCreateCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSessionRepository)(nil).Create), arg0, arg1, arg2) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSessionRepository)(nil).Create), ctx, client, user) return &MockSessionRepositoryCreateCall{Call: call} } @@ -157,17 +158,17 @@ func (c *MockSessionRepositoryCreatedAtColumnCall) DoAndReturn(f func() database } // CreatedAtCondition mocks base method. -func (m *MockSessionRepository) CreatedAtCondition(arg0 database.NumberOperation, arg1 time.Time) database.Condition { +func (m *MockSessionRepository) CreatedAtCondition(op database.NumberOperation, createdAt time.Time) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreatedAtCondition", arg0, arg1) + ret := m.ctrl.Call(m, "CreatedAtCondition", op, createdAt) ret0, _ := ret[0].(database.Condition) return ret0 } // CreatedAtCondition indicates an expected call of CreatedAtCondition. -func (mr *MockSessionRepositoryMockRecorder) CreatedAtCondition(arg0, arg1 any) *MockSessionRepositoryCreatedAtConditionCall { +func (mr *MockSessionRepositoryMockRecorder) CreatedAtCondition(op, createdAt any) *MockSessionRepositoryCreatedAtConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatedAtCondition", reflect.TypeOf((*MockSessionRepository)(nil).CreatedAtCondition), arg0, arg1) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatedAtCondition", reflect.TypeOf((*MockSessionRepository)(nil).CreatedAtCondition), op, createdAt) return &MockSessionRepositoryCreatedAtConditionCall{Call: call} } @@ -195,17 +196,17 @@ func (c *MockSessionRepositoryCreatedAtConditionCall) DoAndReturn(f func(databas } // CreatorIDCondition mocks base method. -func (m *MockSessionRepository) CreatorIDCondition(arg0 string) database.Condition { +func (m *MockSessionRepository) CreatorIDCondition(creatorID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreatorIDCondition", arg0) + ret := m.ctrl.Call(m, "CreatorIDCondition", creatorID) ret0, _ := ret[0].(database.Condition) return ret0 } // CreatorIDCondition indicates an expected call of CreatorIDCondition. -func (mr *MockSessionRepositoryMockRecorder) CreatorIDCondition(arg0 any) *MockSessionRepositoryCreatorIDConditionCall { +func (mr *MockSessionRepositoryMockRecorder) CreatorIDCondition(creatorID any) *MockSessionRepositoryCreatorIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatorIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).CreatorIDCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatorIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).CreatorIDCondition), creatorID) return &MockSessionRepositoryCreatorIDConditionCall{Call: call} } @@ -233,18 +234,18 @@ func (c *MockSessionRepositoryCreatorIDConditionCall) DoAndReturn(f func(string) } // Delete mocks base method. -func (m *MockSessionRepository) Delete(arg0 context.Context, arg1 database.QueryExecutor, arg2 database.Condition) (int64, error) { +func (m *MockSessionRepository) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "Delete", ctx, client, condition) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // Delete indicates an expected call of Delete. -func (mr *MockSessionRepositoryMockRecorder) Delete(arg0, arg1, arg2 any) *MockSessionRepositoryDeleteCall { +func (mr *MockSessionRepositoryMockRecorder) Delete(ctx, client, condition any) *MockSessionRepositoryDeleteCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSessionRepository)(nil).Delete), arg0, arg1, arg2) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSessionRepository)(nil).Delete), ctx, client, condition) return &MockSessionRepositoryDeleteCall{Call: call} } @@ -272,17 +273,17 @@ func (c *MockSessionRepositoryDeleteCall) DoAndReturn(f func(context.Context, da } // ExistsFactor mocks base method. -func (m *MockSessionRepository) ExistsFactor(arg0 database.Condition) database.Condition { +func (m *MockSessionRepository) ExistsFactor(condition database.Condition) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExistsFactor", arg0) + ret := m.ctrl.Call(m, "ExistsFactor", condition) ret0, _ := ret[0].(database.Condition) return ret0 } // ExistsFactor indicates an expected call of ExistsFactor. -func (mr *MockSessionRepositoryMockRecorder) ExistsFactor(arg0 any) *MockSessionRepositoryExistsFactorCall { +func (mr *MockSessionRepositoryMockRecorder) ExistsFactor(condition any) *MockSessionRepositoryExistsFactorCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsFactor", reflect.TypeOf((*MockSessionRepository)(nil).ExistsFactor), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsFactor", reflect.TypeOf((*MockSessionRepository)(nil).ExistsFactor), condition) return &MockSessionRepositoryExistsFactorCall{Call: call} } @@ -310,17 +311,17 @@ func (c *MockSessionRepositoryExistsFactorCall) DoAndReturn(f func(database.Cond } // ExistsMetadata mocks base method. -func (m *MockSessionRepository) ExistsMetadata(arg0 database.Condition) database.Condition { +func (m *MockSessionRepository) ExistsMetadata(condition database.Condition) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExistsMetadata", arg0) + ret := m.ctrl.Call(m, "ExistsMetadata", condition) ret0, _ := ret[0].(database.Condition) return ret0 } // ExistsMetadata indicates an expected call of ExistsMetadata. -func (mr *MockSessionRepositoryMockRecorder) ExistsMetadata(arg0 any) *MockSessionRepositoryExistsMetadataCall { +func (mr *MockSessionRepositoryMockRecorder) ExistsMetadata(condition any) *MockSessionRepositoryExistsMetadataCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsMetadata", reflect.TypeOf((*MockSessionRepository)(nil).ExistsMetadata), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsMetadata", reflect.TypeOf((*MockSessionRepository)(nil).ExistsMetadata), condition) return &MockSessionRepositoryExistsMetadataCall{Call: call} } @@ -386,17 +387,17 @@ func (c *MockSessionRepositoryExpirationColumnCall) DoAndReturn(f func() databas } // ExpirationCondition mocks base method. -func (m *MockSessionRepository) ExpirationCondition(arg0 database.NumberOperation, arg1 time.Time) database.Condition { +func (m *MockSessionRepository) ExpirationCondition(op database.NumberOperation, expiration time.Time) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExpirationCondition", arg0, arg1) + ret := m.ctrl.Call(m, "ExpirationCondition", op, expiration) ret0, _ := ret[0].(database.Condition) return ret0 } // ExpirationCondition indicates an expected call of ExpirationCondition. -func (mr *MockSessionRepositoryMockRecorder) ExpirationCondition(arg0, arg1 any) *MockSessionRepositoryExpirationConditionCall { +func (mr *MockSessionRepositoryMockRecorder) ExpirationCondition(op, expiration any) *MockSessionRepositoryExpirationConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpirationCondition", reflect.TypeOf((*MockSessionRepository)(nil).ExpirationCondition), arg0, arg1) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpirationCondition", reflect.TypeOf((*MockSessionRepository)(nil).ExpirationCondition), op, expiration) return &MockSessionRepositoryExpirationConditionCall{Call: call} } @@ -462,10 +463,10 @@ func (c *MockSessionRepositoryFactorConditionsCall) DoAndReturn(f func() domain. } // Get mocks base method. -func (m *MockSessionRepository) Get(arg0 context.Context, arg1 database.QueryExecutor, arg2 ...database.QueryOption) (*domain.Session, error) { +func (m *MockSessionRepository) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.Session, error) { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, client} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Get", varargs...) @@ -475,9 +476,9 @@ func (m *MockSessionRepository) Get(arg0 context.Context, arg1 database.QueryExe } // Get indicates an expected call of Get. -func (mr *MockSessionRepositoryMockRecorder) Get(arg0, arg1 any, arg2 ...any) *MockSessionRepositoryGetCall { +func (mr *MockSessionRepositoryMockRecorder) Get(ctx, client any, opts ...any) *MockSessionRepositoryGetCall { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, client}, opts...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSessionRepository)(nil).Get), varargs...) return &MockSessionRepositoryGetCall{Call: call} } @@ -544,17 +545,17 @@ func (c *MockSessionRepositoryIDColumnCall) DoAndReturn(f func() database.Column } // IDCondition mocks base method. -func (m *MockSessionRepository) IDCondition(arg0 string) database.Condition { +func (m *MockSessionRepository) IDCondition(sessionID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IDCondition", arg0) + ret := m.ctrl.Call(m, "IDCondition", sessionID) ret0, _ := ret[0].(database.Condition) return ret0 } // IDCondition indicates an expected call of IDCondition. -func (mr *MockSessionRepositoryMockRecorder) IDCondition(arg0 any) *MockSessionRepositoryIDConditionCall { +func (mr *MockSessionRepositoryMockRecorder) IDCondition(sessionID any) *MockSessionRepositoryIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDCondition", reflect.TypeOf((*MockSessionRepository)(nil).IDCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDCondition", reflect.TypeOf((*MockSessionRepository)(nil).IDCondition), sessionID) return &MockSessionRepositoryIDConditionCall{Call: call} } @@ -620,17 +621,17 @@ func (c *MockSessionRepositoryInstanceIDColumnCall) DoAndReturn(f func() databas } // InstanceIDCondition mocks base method. -func (m *MockSessionRepository) InstanceIDCondition(arg0 string) database.Condition { +func (m *MockSessionRepository) InstanceIDCondition(instanceID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InstanceIDCondition", arg0) + ret := m.ctrl.Call(m, "InstanceIDCondition", instanceID) ret0, _ := ret[0].(database.Condition) return ret0 } // InstanceIDCondition indicates an expected call of InstanceIDCondition. -func (mr *MockSessionRepositoryMockRecorder) InstanceIDCondition(arg0 any) *MockSessionRepositoryInstanceIDConditionCall { +func (mr *MockSessionRepositoryMockRecorder) InstanceIDCondition(instanceID any) *MockSessionRepositoryInstanceIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).InstanceIDCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).InstanceIDCondition), instanceID) return &MockSessionRepositoryInstanceIDConditionCall{Call: call} } @@ -696,10 +697,10 @@ func (c *MockSessionRepositoryLifetimeColumnCall) DoAndReturn(f func() database. } // List mocks base method. -func (m *MockSessionRepository) List(arg0 context.Context, arg1 database.QueryExecutor, arg2 ...database.QueryOption) ([]*domain.Session, error) { +func (m *MockSessionRepository) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.Session, error) { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, client} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "List", varargs...) @@ -709,9 +710,9 @@ func (m *MockSessionRepository) List(arg0 context.Context, arg1 database.QueryEx } // List indicates an expected call of List. -func (mr *MockSessionRepositoryMockRecorder) List(arg0, arg1 any, arg2 ...any) *MockSessionRepositoryListCall { +func (mr *MockSessionRepositoryMockRecorder) List(ctx, client any, opts ...any) *MockSessionRepositoryListCall { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, client}, opts...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockSessionRepository)(nil).List), varargs...) return &MockSessionRepositoryListCall{Call: call} } @@ -816,17 +817,17 @@ func (c *MockSessionRepositoryPrimaryKeyColumnsCall) DoAndReturn(f func() []data } // PrimaryKeyCondition mocks base method. -func (m *MockSessionRepository) PrimaryKeyCondition(arg0, arg1 string) database.Condition { +func (m *MockSessionRepository) PrimaryKeyCondition(instanceID, sessionID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PrimaryKeyCondition", arg0, arg1) + ret := m.ctrl.Call(m, "PrimaryKeyCondition", instanceID, sessionID) ret0, _ := ret[0].(database.Condition) return ret0 } // PrimaryKeyCondition indicates an expected call of PrimaryKeyCondition. -func (mr *MockSessionRepositoryMockRecorder) PrimaryKeyCondition(arg0, arg1 any) *MockSessionRepositoryPrimaryKeyConditionCall { +func (mr *MockSessionRepositoryMockRecorder) PrimaryKeyCondition(instanceID, sessionID any) *MockSessionRepositoryPrimaryKeyConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyCondition", reflect.TypeOf((*MockSessionRepository)(nil).PrimaryKeyCondition), arg0, arg1) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyCondition", reflect.TypeOf((*MockSessionRepository)(nil).PrimaryKeyCondition), instanceID, sessionID) return &MockSessionRepositoryPrimaryKeyConditionCall{Call: call} } @@ -854,17 +855,17 @@ func (c *MockSessionRepositoryPrimaryKeyConditionCall) DoAndReturn(f func(string } // SetChallenge mocks base method. -func (m *MockSessionRepository) SetChallenge(arg0 domain.SessionChallenge) database.Change { +func (m *MockSessionRepository) SetChallenge(challenge domain.SessionChallenge) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetChallenge", arg0) + ret := m.ctrl.Call(m, "SetChallenge", challenge) ret0, _ := ret[0].(database.Change) return ret0 } // SetChallenge indicates an expected call of SetChallenge. -func (mr *MockSessionRepositoryMockRecorder) SetChallenge(arg0 any) *MockSessionRepositorySetChallengeCall { +func (mr *MockSessionRepositoryMockRecorder) SetChallenge(challenge any) *MockSessionRepositorySetChallengeCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetChallenge", reflect.TypeOf((*MockSessionRepository)(nil).SetChallenge), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetChallenge", reflect.TypeOf((*MockSessionRepository)(nil).SetChallenge), challenge) return &MockSessionRepositorySetChallengeCall{Call: call} } @@ -892,17 +893,17 @@ func (c *MockSessionRepositorySetChallengeCall) DoAndReturn(f func(domain.Sessio } // SetFactor mocks base method. -func (m *MockSessionRepository) SetFactor(arg0 domain.SessionFactor) database.Change { +func (m *MockSessionRepository) SetFactor(factor domain.SessionFactor) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetFactor", arg0) + ret := m.ctrl.Call(m, "SetFactor", factor) ret0, _ := ret[0].(database.Change) return ret0 } // SetFactor indicates an expected call of SetFactor. -func (mr *MockSessionRepositoryMockRecorder) SetFactor(arg0 any) *MockSessionRepositorySetFactorCall { +func (mr *MockSessionRepositoryMockRecorder) SetFactor(factor any) *MockSessionRepositorySetFactorCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFactor", reflect.TypeOf((*MockSessionRepository)(nil).SetFactor), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFactor", reflect.TypeOf((*MockSessionRepository)(nil).SetFactor), factor) return &MockSessionRepositorySetFactorCall{Call: call} } @@ -930,17 +931,17 @@ func (c *MockSessionRepositorySetFactorCall) DoAndReturn(f func(domain.SessionFa } // SetLifetime mocks base method. -func (m *MockSessionRepository) SetLifetime(arg0 time.Duration) database.Change { +func (m *MockSessionRepository) SetLifetime(lifetime time.Duration) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetLifetime", arg0) + ret := m.ctrl.Call(m, "SetLifetime", lifetime) ret0, _ := ret[0].(database.Change) return ret0 } // SetLifetime indicates an expected call of SetLifetime. -func (mr *MockSessionRepositoryMockRecorder) SetLifetime(arg0 any) *MockSessionRepositorySetLifetimeCall { +func (mr *MockSessionRepositoryMockRecorder) SetLifetime(lifetime any) *MockSessionRepositorySetLifetimeCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLifetime", reflect.TypeOf((*MockSessionRepository)(nil).SetLifetime), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLifetime", reflect.TypeOf((*MockSessionRepository)(nil).SetLifetime), lifetime) return &MockSessionRepositorySetLifetimeCall{Call: call} } @@ -968,17 +969,17 @@ func (c *MockSessionRepositorySetLifetimeCall) DoAndReturn(f func(time.Duration) } // SetMetadata mocks base method. -func (m *MockSessionRepository) SetMetadata(arg0 []*domain.SessionMetadata) database.Change { +func (m *MockSessionRepository) SetMetadata(metadata []*domain.SessionMetadata) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetMetadata", arg0) + ret := m.ctrl.Call(m, "SetMetadata", metadata) ret0, _ := ret[0].(database.Change) return ret0 } // SetMetadata indicates an expected call of SetMetadata. -func (mr *MockSessionRepositoryMockRecorder) SetMetadata(arg0 any) *MockSessionRepositorySetMetadataCall { +func (mr *MockSessionRepositoryMockRecorder) SetMetadata(metadata any) *MockSessionRepositorySetMetadataCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMetadata", reflect.TypeOf((*MockSessionRepository)(nil).SetMetadata), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMetadata", reflect.TypeOf((*MockSessionRepository)(nil).SetMetadata), metadata) return &MockSessionRepositorySetMetadataCall{Call: call} } @@ -1006,17 +1007,17 @@ func (c *MockSessionRepositorySetMetadataCall) DoAndReturn(f func([]*domain.Sess } // SetToken mocks base method. -func (m *MockSessionRepository) SetToken(arg0 string) database.Change { +func (m *MockSessionRepository) SetToken(token string) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetToken", arg0) + ret := m.ctrl.Call(m, "SetToken", token) ret0, _ := ret[0].(database.Change) return ret0 } // SetToken indicates an expected call of SetToken. -func (mr *MockSessionRepositoryMockRecorder) SetToken(arg0 any) *MockSessionRepositorySetTokenCall { +func (mr *MockSessionRepositoryMockRecorder) SetToken(token any) *MockSessionRepositorySetTokenCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToken", reflect.TypeOf((*MockSessionRepository)(nil).SetToken), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToken", reflect.TypeOf((*MockSessionRepository)(nil).SetToken), token) return &MockSessionRepositorySetTokenCall{Call: call} } @@ -1044,17 +1045,17 @@ func (c *MockSessionRepositorySetTokenCall) DoAndReturn(f func(string) database. } // SetUpdatedAt mocks base method. -func (m *MockSessionRepository) SetUpdatedAt(arg0 time.Time) database.Change { +func (m *MockSessionRepository) SetUpdatedAt(updatedAt time.Time) database.Change { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetUpdatedAt", arg0) + ret := m.ctrl.Call(m, "SetUpdatedAt", updatedAt) ret0, _ := ret[0].(database.Change) return ret0 } // SetUpdatedAt indicates an expected call of SetUpdatedAt. -func (mr *MockSessionRepositoryMockRecorder) SetUpdatedAt(arg0 any) *MockSessionRepositorySetUpdatedAtCall { +func (mr *MockSessionRepositoryMockRecorder) SetUpdatedAt(updatedAt any) *MockSessionRepositorySetUpdatedAtCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdatedAt", reflect.TypeOf((*MockSessionRepository)(nil).SetUpdatedAt), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdatedAt", reflect.TypeOf((*MockSessionRepository)(nil).SetUpdatedAt), updatedAt) return &MockSessionRepositorySetUpdatedAtCall{Call: call} } @@ -1081,49 +1082,49 @@ func (c *MockSessionRepositorySetUpdatedAtCall) DoAndReturn(f func(time.Time) da return c } -// TokenColumn mocks base method. -func (m *MockSessionRepository) TokenColumn() database.Column { +// TokenIDColumn mocks base method. +func (m *MockSessionRepository) TokenIDColumn() database.Column { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TokenColumn") + ret := m.ctrl.Call(m, "TokenIDColumn") ret0, _ := ret[0].(database.Column) return ret0 } -// TokenColumn indicates an expected call of TokenColumn. -func (mr *MockSessionRepositoryMockRecorder) TokenColumn() *MockSessionRepositoryTokenColumnCall { +// TokenIDColumn indicates an expected call of TokenIDColumn. +func (mr *MockSessionRepositoryMockRecorder) TokenIDColumn() *MockSessionRepositoryTokenIDColumnCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TokenColumn", reflect.TypeOf((*MockSessionRepository)(nil).TokenColumn)) - return &MockSessionRepositoryTokenColumnCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TokenIDColumn", reflect.TypeOf((*MockSessionRepository)(nil).TokenIDColumn)) + return &MockSessionRepositoryTokenIDColumnCall{Call: call} } -// MockSessionRepositoryTokenColumnCall wrap *gomock.Call -type MockSessionRepositoryTokenColumnCall struct { +// MockSessionRepositoryTokenIDColumnCall wrap *gomock.Call +type MockSessionRepositoryTokenIDColumnCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockSessionRepositoryTokenColumnCall) Return(arg0 database.Column) *MockSessionRepositoryTokenColumnCall { +func (c *MockSessionRepositoryTokenIDColumnCall) Return(arg0 database.Column) *MockSessionRepositoryTokenIDColumnCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockSessionRepositoryTokenColumnCall) Do(f func() database.Column) *MockSessionRepositoryTokenColumnCall { +func (c *MockSessionRepositoryTokenIDColumnCall) Do(f func() database.Column) *MockSessionRepositoryTokenIDColumnCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockSessionRepositoryTokenColumnCall) DoAndReturn(f func() database.Column) *MockSessionRepositoryTokenColumnCall { +func (c *MockSessionRepositoryTokenIDColumnCall) DoAndReturn(f func() database.Column) *MockSessionRepositoryTokenIDColumnCall { c.Call = c.Call.DoAndReturn(f) return c } // Update mocks base method. -func (m *MockSessionRepository) Update(arg0 context.Context, arg1 database.QueryExecutor, arg2 database.Condition, arg3 ...database.Change) (int64, error) { +func (m *MockSessionRepository) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { m.ctrl.T.Helper() - varargs := []any{arg0, arg1, arg2} - for _, a := range arg3 { + varargs := []any{ctx, client, condition} + for _, a := range changes { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) @@ -1133,9 +1134,9 @@ func (m *MockSessionRepository) Update(arg0 context.Context, arg1 database.Query } // Update indicates an expected call of Update. -func (mr *MockSessionRepositoryMockRecorder) Update(arg0, arg1, arg2 any, arg3 ...any) *MockSessionRepositoryUpdateCall { +func (mr *MockSessionRepositoryMockRecorder) Update(ctx, client, condition any, changes ...any) *MockSessionRepositoryUpdateCall { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1, arg2}, arg3...) + varargs := append([]any{ctx, client, condition}, changes...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSessionRepository)(nil).Update), varargs...) return &MockSessionRepositoryUpdateCall{Call: call} } @@ -1202,17 +1203,17 @@ func (c *MockSessionRepositoryUpdatedAtColumnCall) DoAndReturn(f func() database } // UpdatedAtCondition mocks base method. -func (m *MockSessionRepository) UpdatedAtCondition(arg0 database.NumberOperation, arg1 time.Time) database.Condition { +func (m *MockSessionRepository) UpdatedAtCondition(op database.NumberOperation, updatedAt time.Time) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdatedAtCondition", arg0, arg1) + ret := m.ctrl.Call(m, "UpdatedAtCondition", op, updatedAt) ret0, _ := ret[0].(database.Condition) return ret0 } // UpdatedAtCondition indicates an expected call of UpdatedAtCondition. -func (mr *MockSessionRepositoryMockRecorder) UpdatedAtCondition(arg0, arg1 any) *MockSessionRepositoryUpdatedAtConditionCall { +func (mr *MockSessionRepositoryMockRecorder) UpdatedAtCondition(op, updatedAt any) *MockSessionRepositoryUpdatedAtConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedAtCondition", reflect.TypeOf((*MockSessionRepository)(nil).UpdatedAtCondition), arg0, arg1) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedAtCondition", reflect.TypeOf((*MockSessionRepository)(nil).UpdatedAtCondition), op, updatedAt) return &MockSessionRepositoryUpdatedAtConditionCall{Call: call} } @@ -1240,17 +1241,17 @@ func (c *MockSessionRepositoryUpdatedAtConditionCall) DoAndReturn(f func(databas } // UserAgentIDCondition mocks base method. -func (m *MockSessionRepository) UserAgentIDCondition(arg0 string) database.Condition { +func (m *MockSessionRepository) UserAgentIDCondition(userAgentID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UserAgentIDCondition", arg0) + ret := m.ctrl.Call(m, "UserAgentIDCondition", userAgentID) ret0, _ := ret[0].(database.Condition) return ret0 } // UserAgentIDCondition indicates an expected call of UserAgentIDCondition. -func (mr *MockSessionRepositoryMockRecorder) UserAgentIDCondition(arg0 any) *MockSessionRepositoryUserAgentIDConditionCall { +func (mr *MockSessionRepositoryMockRecorder) UserAgentIDCondition(userAgentID any) *MockSessionRepositoryUserAgentIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserAgentIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).UserAgentIDCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserAgentIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).UserAgentIDCondition), userAgentID) return &MockSessionRepositoryUserAgentIDConditionCall{Call: call} } @@ -1316,17 +1317,17 @@ func (c *MockSessionRepositoryUserIDColumnCall) DoAndReturn(f func() database.Co } // UserIDCondition mocks base method. -func (m *MockSessionRepository) UserIDCondition(arg0 string) database.Condition { +func (m *MockSessionRepository) UserIDCondition(userID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UserIDCondition", arg0) + ret := m.ctrl.Call(m, "UserIDCondition", userID) ret0, _ := ret[0].(database.Condition) return ret0 } // UserIDCondition indicates an expected call of UserIDCondition. -func (mr *MockSessionRepositoryMockRecorder) UserIDCondition(arg0 any) *MockSessionRepositoryUserIDConditionCall { +func (mr *MockSessionRepositoryMockRecorder) UserIDCondition(userID any) *MockSessionRepositoryUserIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).UserIDCondition), arg0) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserIDCondition", reflect.TypeOf((*MockSessionRepository)(nil).UserIDCondition), userID) return &MockSessionRepositoryUserIDConditionCall{Call: call} } diff --git a/backend/v3/domain/mock/user.mock.go b/backend/v3/domain/mock/user.mock.go index d83a1372a..db420d532 100644 --- a/backend/v3/domain/mock/user.mock.go +++ b/backend/v3/domain/mock/user.mock.go @@ -320,6 +320,44 @@ func (c *MockUserRepositoryHumanCall) DoAndReturn(f func() domain.HumanUserRepos return c } +// IDColumn mocks base method. +func (m *MockUserRepository) IDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// IDColumn indicates an expected call of IDColumn. +func (mr *MockUserRepositoryMockRecorder) IDColumn() *MockUserRepositoryIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDColumn", reflect.TypeOf((*MockUserRepository)(nil).IDColumn)) + return &MockUserRepositoryIDColumnCall{Call: call} +} + +// MockUserRepositoryIDColumnCall wrap *gomock.Call +type MockUserRepositoryIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserRepositoryIDColumnCall) Return(arg0 database.Column) *MockUserRepositoryIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserRepositoryIDColumnCall) Do(f func() database.Column) *MockUserRepositoryIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserRepositoryIDColumnCall) DoAndReturn(f func() database.Column) *MockUserRepositoryIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // IDCondition mocks base method. func (m *MockUserRepository) IDCondition(userID string) database.Condition { m.ctrl.T.Helper() @@ -358,6 +396,44 @@ func (c *MockUserRepositoryIDConditionCall) DoAndReturn(f func(string) database. return c } +// InstanceIDColumn mocks base method. +func (m *MockUserRepository) InstanceIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// InstanceIDColumn indicates an expected call of InstanceIDColumn. +func (mr *MockUserRepositoryMockRecorder) InstanceIDColumn() *MockUserRepositoryInstanceIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDColumn", reflect.TypeOf((*MockUserRepository)(nil).InstanceIDColumn)) + return &MockUserRepositoryInstanceIDColumnCall{Call: call} +} + +// MockUserRepositoryInstanceIDColumnCall wrap *gomock.Call +type MockUserRepositoryInstanceIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserRepositoryInstanceIDColumnCall) Return(arg0 database.Column) *MockUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserRepositoryInstanceIDColumnCall) Do(f func() database.Column) *MockUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserRepositoryInstanceIDColumnCall) DoAndReturn(f func() database.Column) *MockUserRepositoryInstanceIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // InstanceIDCondition mocks base method. func (m *MockUserRepository) InstanceIDCondition(instanceID string) database.Condition { m.ctrl.T.Helper() @@ -440,234 +516,6 @@ func (c *MockUserRepositoryListCall) DoAndReturn(f func(context.Context, databas return c } -// LoadIdentityProviderLinks mocks base method. -func (m *MockUserRepository) LoadIdentityProviderLinks() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadIdentityProviderLinks") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadIdentityProviderLinks indicates an expected call of LoadIdentityProviderLinks. -func (mr *MockUserRepositoryMockRecorder) LoadIdentityProviderLinks() *MockUserRepositoryLoadIdentityProviderLinksCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadIdentityProviderLinks", reflect.TypeOf((*MockUserRepository)(nil).LoadIdentityProviderLinks)) - return &MockUserRepositoryLoadIdentityProviderLinksCall{Call: call} -} - -// MockUserRepositoryLoadIdentityProviderLinksCall wrap *gomock.Call -type MockUserRepositoryLoadIdentityProviderLinksCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadIdentityProviderLinksCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadIdentityProviderLinksCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadIdentityProviderLinksCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadIdentityProviderLinksCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadIdentityProviderLinksCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadIdentityProviderLinksCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// LoadKeys mocks base method. -func (m *MockUserRepository) LoadKeys() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadKeys") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadKeys indicates an expected call of LoadKeys. -func (mr *MockUserRepositoryMockRecorder) LoadKeys() *MockUserRepositoryLoadKeysCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadKeys", reflect.TypeOf((*MockUserRepository)(nil).LoadKeys)) - return &MockUserRepositoryLoadKeysCall{Call: call} -} - -// MockUserRepositoryLoadKeysCall wrap *gomock.Call -type MockUserRepositoryLoadKeysCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadKeysCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadKeysCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadKeysCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadKeysCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadKeysCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadKeysCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// LoadMetadata mocks base method. -func (m *MockUserRepository) LoadMetadata() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadMetadata") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadMetadata indicates an expected call of LoadMetadata. -func (mr *MockUserRepositoryMockRecorder) LoadMetadata() *MockUserRepositoryLoadMetadataCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadMetadata", reflect.TypeOf((*MockUserRepository)(nil).LoadMetadata)) - return &MockUserRepositoryLoadMetadataCall{Call: call} -} - -// MockUserRepositoryLoadMetadataCall wrap *gomock.Call -type MockUserRepositoryLoadMetadataCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadMetadataCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadMetadataCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadMetadataCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadMetadataCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadMetadataCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadMetadataCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// LoadPATs mocks base method. -func (m *MockUserRepository) LoadPATs() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadPATs") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadPATs indicates an expected call of LoadPATs. -func (mr *MockUserRepositoryMockRecorder) LoadPATs() *MockUserRepositoryLoadPATsCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPATs", reflect.TypeOf((*MockUserRepository)(nil).LoadPATs)) - return &MockUserRepositoryLoadPATsCall{Call: call} -} - -// MockUserRepositoryLoadPATsCall wrap *gomock.Call -type MockUserRepositoryLoadPATsCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadPATsCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadPATsCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadPATsCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadPATsCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadPATsCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadPATsCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// LoadPasskeys mocks base method. -func (m *MockUserRepository) LoadPasskeys() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadPasskeys") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadPasskeys indicates an expected call of LoadPasskeys. -func (mr *MockUserRepositoryMockRecorder) LoadPasskeys() *MockUserRepositoryLoadPasskeysCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPasskeys", reflect.TypeOf((*MockUserRepository)(nil).LoadPasskeys)) - return &MockUserRepositoryLoadPasskeysCall{Call: call} -} - -// MockUserRepositoryLoadPasskeysCall wrap *gomock.Call -type MockUserRepositoryLoadPasskeysCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadPasskeysCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadPasskeysCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadPasskeysCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadPasskeysCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadPasskeysCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadPasskeysCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// LoadVerifications mocks base method. -func (m *MockUserRepository) LoadVerifications() domain.UserRepository { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadVerifications") - ret0, _ := ret[0].(domain.UserRepository) - return ret0 -} - -// LoadVerifications indicates an expected call of LoadVerifications. -func (mr *MockUserRepositoryMockRecorder) LoadVerifications() *MockUserRepositoryLoadVerificationsCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadVerifications", reflect.TypeOf((*MockUserRepository)(nil).LoadVerifications)) - return &MockUserRepositoryLoadVerificationsCall{Call: call} -} - -// MockUserRepositoryLoadVerificationsCall wrap *gomock.Call -type MockUserRepositoryLoadVerificationsCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryLoadVerificationsCall) Return(arg0 domain.UserRepository) *MockUserRepositoryLoadVerificationsCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryLoadVerificationsCall) Do(f func() domain.UserRepository) *MockUserRepositoryLoadVerificationsCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryLoadVerificationsCall) DoAndReturn(f func() domain.UserRepository) *MockUserRepositoryLoadVerificationsCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - // LoginNameCondition mocks base method. func (m *MockUserRepository) LoginNameCondition(op database.TextOperation, loginName string) database.Condition { m.ctrl.T.Helper() @@ -744,116 +592,78 @@ func (c *MockUserRepositoryMachineCall) DoAndReturn(f func() domain.MachineUserR return c } -// MetadataKeyCondition mocks base method. -func (m *MockUserRepository) MetadataKeyCondition(key string) database.Condition { +// MetadataConditions mocks base method. +func (m *MockUserRepository) MetadataConditions() domain.UserMetadataConditions { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataKeyCondition", key) - ret0, _ := ret[0].(database.Condition) + ret := m.ctrl.Call(m, "MetadataConditions") + ret0, _ := ret[0].(domain.UserMetadataConditions) return ret0 } -// MetadataKeyCondition indicates an expected call of MetadataKeyCondition. -func (mr *MockUserRepositoryMockRecorder) MetadataKeyCondition(key any) *MockUserRepositoryMetadataKeyConditionCall { +// MetadataConditions indicates an expected call of MetadataConditions. +func (mr *MockUserRepositoryMockRecorder) MetadataConditions() *MockUserRepositoryMetadataConditionsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataKeyCondition", reflect.TypeOf((*MockUserRepository)(nil).MetadataKeyCondition), key) - return &MockUserRepositoryMetadataKeyConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataConditions", reflect.TypeOf((*MockUserRepository)(nil).MetadataConditions)) + return &MockUserRepositoryMetadataConditionsCall{Call: call} } -// MockUserRepositoryMetadataKeyConditionCall wrap *gomock.Call -type MockUserRepositoryMetadataKeyConditionCall struct { +// MockUserRepositoryMetadataConditionsCall wrap *gomock.Call +type MockUserRepositoryMetadataConditionsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryMetadataKeyConditionCall) Return(arg0 database.Condition) *MockUserRepositoryMetadataKeyConditionCall { +func (c *MockUserRepositoryMetadataConditionsCall) Return(arg0 domain.UserMetadataConditions) *MockUserRepositoryMetadataConditionsCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryMetadataKeyConditionCall) Do(f func(string) database.Condition) *MockUserRepositoryMetadataKeyConditionCall { +func (c *MockUserRepositoryMetadataConditionsCall) Do(f func() domain.UserMetadataConditions) *MockUserRepositoryMetadataConditionsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryMetadataKeyConditionCall) DoAndReturn(f func(string) database.Condition) *MockUserRepositoryMetadataKeyConditionCall { +func (c *MockUserRepositoryMetadataConditionsCall) DoAndReturn(f func() domain.UserMetadataConditions) *MockUserRepositoryMetadataConditionsCall { c.Call = c.Call.DoAndReturn(f) return c } -// MetadataValueCondition mocks base method. -func (m *MockUserRepository) MetadataValueCondition(op database.BytesOperation, value []byte) database.Condition { +// OrganizationIDCondition mocks base method. +func (m *MockUserRepository) OrganizationIDCondition(orgID string) database.Condition { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MetadataValueCondition", op, value) + ret := m.ctrl.Call(m, "OrganizationIDCondition", orgID) ret0, _ := ret[0].(database.Condition) return ret0 } -// MetadataValueCondition indicates an expected call of MetadataValueCondition. -func (mr *MockUserRepositoryMockRecorder) MetadataValueCondition(op, value any) *MockUserRepositoryMetadataValueConditionCall { +// OrganizationIDCondition indicates an expected call of OrganizationIDCondition. +func (mr *MockUserRepositoryMockRecorder) OrganizationIDCondition(orgID any) *MockUserRepositoryOrganizationIDConditionCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MetadataValueCondition", reflect.TypeOf((*MockUserRepository)(nil).MetadataValueCondition), op, value) - return &MockUserRepositoryMetadataValueConditionCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrganizationIDCondition", reflect.TypeOf((*MockUserRepository)(nil).OrganizationIDCondition), orgID) + return &MockUserRepositoryOrganizationIDConditionCall{Call: call} } -// MockUserRepositoryMetadataValueConditionCall wrap *gomock.Call -type MockUserRepositoryMetadataValueConditionCall struct { +// MockUserRepositoryOrganizationIDConditionCall wrap *gomock.Call +type MockUserRepositoryOrganizationIDConditionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryMetadataValueConditionCall) Return(arg0 database.Condition) *MockUserRepositoryMetadataValueConditionCall { +func (c *MockUserRepositoryOrganizationIDConditionCall) Return(arg0 database.Condition) *MockUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryMetadataValueConditionCall) Do(f func(database.BytesOperation, []byte) database.Condition) *MockUserRepositoryMetadataValueConditionCall { +func (c *MockUserRepositoryOrganizationIDConditionCall) Do(f func(string) database.Condition) *MockUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryMetadataValueConditionCall) DoAndReturn(f func(database.BytesOperation, []byte) database.Condition) *MockUserRepositoryMetadataValueConditionCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - -// OrgIDCondition mocks base method. -func (m *MockUserRepository) OrgIDCondition(orgID string) database.Condition { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OrgIDCondition", orgID) - ret0, _ := ret[0].(database.Condition) - return ret0 -} - -// OrgIDCondition indicates an expected call of OrgIDCondition. -func (mr *MockUserRepositoryMockRecorder) OrgIDCondition(orgID any) *MockUserRepositoryOrgIDConditionCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrgIDCondition", reflect.TypeOf((*MockUserRepository)(nil).OrgIDCondition), orgID) - return &MockUserRepositoryOrgIDConditionCall{Call: call} -} - -// MockUserRepositoryOrgIDConditionCall wrap *gomock.Call -type MockUserRepositoryOrgIDConditionCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockUserRepositoryOrgIDConditionCall) Return(arg0 database.Condition) *MockUserRepositoryOrgIDConditionCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockUserRepositoryOrgIDConditionCall) Do(f func(string) database.Condition) *MockUserRepositoryOrgIDConditionCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockUserRepositoryOrgIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockUserRepositoryOrgIDConditionCall { +func (c *MockUserRepositoryOrganizationIDConditionCall) DoAndReturn(f func(string) database.Condition) *MockUserRepositoryOrganizationIDConditionCall { c.Call = c.Call.DoAndReturn(f) return c } @@ -1162,6 +972,44 @@ func (c *MockUserRepositoryStateConditionCall) DoAndReturn(f func(domain.UserSta return c } +// TypeColumn mocks base method. +func (m *MockUserRepository) TypeColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TypeColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// TypeColumn indicates an expected call of TypeColumn. +func (mr *MockUserRepositoryMockRecorder) TypeColumn() *MockUserRepositoryTypeColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TypeColumn", reflect.TypeOf((*MockUserRepository)(nil).TypeColumn)) + return &MockUserRepositoryTypeColumnCall{Call: call} +} + +// MockUserRepositoryTypeColumnCall wrap *gomock.Call +type MockUserRepositoryTypeColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserRepositoryTypeColumnCall) Return(arg0 database.Column) *MockUserRepositoryTypeColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserRepositoryTypeColumnCall) Do(f func() database.Column) *MockUserRepositoryTypeColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserRepositoryTypeColumnCall) DoAndReturn(f func() database.Column) *MockUserRepositoryTypeColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // TypeCondition mocks base method. func (m *MockUserRepository) TypeCondition(userType domain.UserType) database.Condition { m.ctrl.T.Helper() diff --git a/backend/v3/domain/mock/user_identity_provider_link.mock.go b/backend/v3/domain/mock/user_identity_provider_link.mock.go new file mode 100644 index 000000000..8c9f37f0f --- /dev/null +++ b/backend/v3/domain/mock/user_identity_provider_link.mock.go @@ -0,0 +1,899 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/zitadel/zitadel/backend/v3/domain (interfaces: UserIdentityProviderLinkRepository) +// +// Generated by this command: +// +// mockgen -typed -package domainmock -destination ./mock/user_identity_provider_link.mock.go . UserIdentityProviderLinkRepository +// + +// Package domainmock is a generated GoMock package. +package domainmock + +import ( + context "context" + reflect "reflect" + time "time" + + domain "github.com/zitadel/zitadel/backend/v3/domain" + database "github.com/zitadel/zitadel/backend/v3/storage/database" + gomock "go.uber.org/mock/gomock" +) + +// MockUserIdentityProviderLinkRepository is a mock of UserIdentityProviderLinkRepository interface. +type MockUserIdentityProviderLinkRepository struct { + ctrl *gomock.Controller + recorder *MockUserIdentityProviderLinkRepositoryMockRecorder + isgomock struct{} +} + +// MockUserIdentityProviderLinkRepositoryMockRecorder is the mock recorder for MockUserIdentityProviderLinkRepository. +type MockUserIdentityProviderLinkRepositoryMockRecorder struct { + mock *MockUserIdentityProviderLinkRepository +} + +// NewMockUserIdentityProviderLinkRepository creates a new mock instance. +func NewMockUserIdentityProviderLinkRepository(ctrl *gomock.Controller) *MockUserIdentityProviderLinkRepository { + mock := &MockUserIdentityProviderLinkRepository{ctrl: ctrl} + mock.recorder = &MockUserIdentityProviderLinkRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockUserIdentityProviderLinkRepository) EXPECT() *MockUserIdentityProviderLinkRepositoryMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockUserIdentityProviderLinkRepository) Create(ctx context.Context, client database.QueryExecutor, link *domain.UserIdentityProviderLink) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, client, link) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) Create(ctx, client, link any) *MockUserIdentityProviderLinkRepositoryCreateCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).Create), ctx, client, link) + return &MockUserIdentityProviderLinkRepositoryCreateCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryCreateCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryCreateCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryCreateCall) Return(arg0 error) *MockUserIdentityProviderLinkRepositoryCreateCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryCreateCall) Do(f func(context.Context, database.QueryExecutor, *domain.UserIdentityProviderLink) error) *MockUserIdentityProviderLinkRepositoryCreateCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryCreateCall) DoAndReturn(f func(context.Context, database.QueryExecutor, *domain.UserIdentityProviderLink) error) *MockUserIdentityProviderLinkRepositoryCreateCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreatedAtColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) CreatedAtColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreatedAtColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// CreatedAtColumn indicates an expected call of CreatedAtColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) CreatedAtColumn() *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatedAtColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).CreatedAtColumn)) + return &MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryCreatedAtColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Delete mocks base method. +func (m *MockUserIdentityProviderLinkRepository) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, client, condition) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) Delete(ctx, client, condition any) *MockUserIdentityProviderLinkRepositoryDeleteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).Delete), ctx, client, condition) + return &MockUserIdentityProviderLinkRepositoryDeleteCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryDeleteCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryDeleteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryDeleteCall) Return(arg0 int64, arg1 error) *MockUserIdentityProviderLinkRepositoryDeleteCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryDeleteCall) Do(f func(context.Context, database.QueryExecutor, database.Condition) (int64, error)) *MockUserIdentityProviderLinkRepositoryDeleteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryDeleteCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition) (int64, error)) *MockUserIdentityProviderLinkRepositoryDeleteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Get mocks base method. +func (m *MockUserIdentityProviderLinkRepository) Get(ctx context.Context, client database.QueryExecutor, condition database.Condition, opts ...database.QueryOption) (*domain.UserIdentityProviderLink, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client, condition} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(*domain.UserIdentityProviderLink) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) Get(ctx, client, condition any, opts ...any) *MockUserIdentityProviderLinkRepositoryGetCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client, condition}, opts...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).Get), varargs...) + return &MockUserIdentityProviderLinkRepositoryGetCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryGetCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryGetCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryGetCall) Return(arg0 *domain.UserIdentityProviderLink, arg1 error) *MockUserIdentityProviderLinkRepositoryGetCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryGetCall) Do(f func(context.Context, database.QueryExecutor, database.Condition, ...database.QueryOption) (*domain.UserIdentityProviderLink, error)) *MockUserIdentityProviderLinkRepositoryGetCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryGetCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition, ...database.QueryOption) (*domain.UserIdentityProviderLink, error)) *MockUserIdentityProviderLinkRepositoryGetCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IdentityProviderIDColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) IdentityProviderIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IdentityProviderIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// IdentityProviderIDColumn indicates an expected call of IdentityProviderIDColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) IdentityProviderIDColumn() *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IdentityProviderIDColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).IdentityProviderIDColumn)) + return &MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// IdentityProviderIDCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) IdentityProviderIDCondition(op database.TextOperation, id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IdentityProviderIDCondition", op, id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// IdentityProviderIDCondition indicates an expected call of IdentityProviderIDCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) IdentityProviderIDCondition(op, id any) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IdentityProviderIDCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).IdentityProviderIDCondition), op, id) + return &MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryIdentityProviderIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// InstanceIDColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) InstanceIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// InstanceIDColumn indicates an expected call of InstanceIDColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) InstanceIDColumn() *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).InstanceIDColumn)) + return &MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryInstanceIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// InstanceIDCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) InstanceIDCondition(op database.TextOperation, id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceIDCondition", op, id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// InstanceIDCondition indicates an expected call of InstanceIDCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) InstanceIDCondition(op, id any) *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).InstanceIDCondition), op, id) + return &MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryInstanceIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// List mocks base method. +func (m *MockUserIdentityProviderLinkRepository) List(ctx context.Context, client database.QueryExecutor, condition database.Condition, opts ...database.QueryOption) ([]*domain.UserIdentityProviderLink, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client, condition} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].([]*domain.UserIdentityProviderLink) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) List(ctx, client, condition any, opts ...any) *MockUserIdentityProviderLinkRepositoryListCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client, condition}, opts...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).List), varargs...) + return &MockUserIdentityProviderLinkRepositoryListCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryListCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryListCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryListCall) Return(arg0 []*domain.UserIdentityProviderLink, arg1 error) *MockUserIdentityProviderLinkRepositoryListCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryListCall) Do(f func(context.Context, database.QueryExecutor, database.Condition, ...database.QueryOption) ([]*domain.UserIdentityProviderLink, error)) *MockUserIdentityProviderLinkRepositoryListCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryListCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition, ...database.QueryOption) ([]*domain.UserIdentityProviderLink, error)) *MockUserIdentityProviderLinkRepositoryListCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// PrimaryKeyColumns mocks base method. +func (m *MockUserIdentityProviderLinkRepository) PrimaryKeyColumns() []database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrimaryKeyColumns") + ret0, _ := ret[0].([]database.Column) + return ret0 +} + +// PrimaryKeyColumns indicates an expected call of PrimaryKeyColumns. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) PrimaryKeyColumns() *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyColumns", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).PrimaryKeyColumns)) + return &MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall) Return(arg0 []database.Column) *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall) Do(f func() []database.Column) *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall) DoAndReturn(f func() []database.Column) *MockUserIdentityProviderLinkRepositoryPrimaryKeyColumnsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// PrimaryKeyCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) PrimaryKeyCondition(instanceID, idpID, providedID string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrimaryKeyCondition", instanceID, idpID, providedID) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// PrimaryKeyCondition indicates an expected call of PrimaryKeyCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) PrimaryKeyCondition(instanceID, idpID, providedID any) *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryKeyCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).PrimaryKeyCondition), instanceID, idpID, providedID) + return &MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall) Do(f func(string, string, string) database.Condition) *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall) DoAndReturn(f func(string, string, string) database.Condition) *MockUserIdentityProviderLinkRepositoryPrimaryKeyConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProvidedIDColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) ProvidedIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProvidedIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// ProvidedIDColumn indicates an expected call of ProvidedIDColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) ProvidedIDColumn() *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedIDColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).ProvidedIDColumn)) + return &MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryProvidedIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProvidedIDCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) ProvidedIDCondition(op database.TextOperation, id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProvidedIDCondition", op, id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ProvidedIDCondition indicates an expected call of ProvidedIDCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) ProvidedIDCondition(op, id any) *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedIDCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).ProvidedIDCondition), op, id) + return &MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProvidedUsernameColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) ProvidedUsernameColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProvidedUsernameColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// ProvidedUsernameColumn indicates an expected call of ProvidedUsernameColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) ProvidedUsernameColumn() *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedUsernameColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).ProvidedUsernameColumn)) + return &MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryProvidedUsernameColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ProvidedUsernameCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) ProvidedUsernameCondition(op database.TextOperation, username string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProvidedUsernameCondition", op, username) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// ProvidedUsernameCondition indicates an expected call of ProvidedUsernameCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) ProvidedUsernameCondition(op, username any) *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvidedUsernameCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).ProvidedUsernameCondition), op, username) + return &MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryProvidedUsernameConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetProvidedUserID mocks base method. +func (m *MockUserIdentityProviderLinkRepository) SetProvidedUserID(providedID string) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetProvidedUserID", providedID) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetProvidedUserID indicates an expected call of SetProvidedUserID. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) SetProvidedUserID(providedID any) *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProvidedUserID", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).SetProvidedUserID), providedID) + return &MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall) Return(arg0 database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall) Do(f func(string) database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall) DoAndReturn(f func(string) database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUserIDCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetProvidedUsername mocks base method. +func (m *MockUserIdentityProviderLinkRepository) SetProvidedUsername(username string) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetProvidedUsername", username) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetProvidedUsername indicates an expected call of SetProvidedUsername. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) SetProvidedUsername(username any) *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProvidedUsername", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).SetProvidedUsername), username) + return &MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall) Return(arg0 database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall) Do(f func(string) database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall) DoAndReturn(f func(string) database.Change) *MockUserIdentityProviderLinkRepositorySetProvidedUsernameCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// SetUpdatedAt mocks base method. +func (m *MockUserIdentityProviderLinkRepository) SetUpdatedAt(updatedAt time.Time) database.Change { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetUpdatedAt", updatedAt) + ret0, _ := ret[0].(database.Change) + return ret0 +} + +// SetUpdatedAt indicates an expected call of SetUpdatedAt. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) SetUpdatedAt(updatedAt any) *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpdatedAt", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).SetUpdatedAt), updatedAt) + return &MockUserIdentityProviderLinkRepositorySetUpdatedAtCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositorySetUpdatedAtCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositorySetUpdatedAtCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall) Return(arg0 database.Change) *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall) Do(f func(time.Time) database.Change) *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall) DoAndReturn(f func(time.Time) database.Change) *MockUserIdentityProviderLinkRepositorySetUpdatedAtCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// Update mocks base method. +func (m *MockUserIdentityProviderLinkRepository) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, client, condition} + for _, a := range changes { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) Update(ctx, client, condition any, changes ...any) *MockUserIdentityProviderLinkRepositoryUpdateCall { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, client, condition}, changes...) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).Update), varargs...) + return &MockUserIdentityProviderLinkRepositoryUpdateCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryUpdateCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryUpdateCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryUpdateCall) Return(arg0 int64, arg1 error) *MockUserIdentityProviderLinkRepositoryUpdateCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryUpdateCall) Do(f func(context.Context, database.QueryExecutor, database.Condition, ...database.Change) (int64, error)) *MockUserIdentityProviderLinkRepositoryUpdateCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryUpdateCall) DoAndReturn(f func(context.Context, database.QueryExecutor, database.Condition, ...database.Change) (int64, error)) *MockUserIdentityProviderLinkRepositoryUpdateCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdatedAtColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) UpdatedAtColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdatedAtColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// UpdatedAtColumn indicates an expected call of UpdatedAtColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) UpdatedAtColumn() *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedAtColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).UpdatedAtColumn)) + return &MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryUpdatedAtColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UserIDColumn mocks base method. +func (m *MockUserIdentityProviderLinkRepository) UserIDColumn() database.Column { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UserIDColumn") + ret0, _ := ret[0].(database.Column) + return ret0 +} + +// UserIDColumn indicates an expected call of UserIDColumn. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) UserIDColumn() *MockUserIdentityProviderLinkRepositoryUserIDColumnCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserIDColumn", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).UserIDColumn)) + return &MockUserIdentityProviderLinkRepositoryUserIDColumnCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryUserIDColumnCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryUserIDColumnCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryUserIDColumnCall) Return(arg0 database.Column) *MockUserIdentityProviderLinkRepositoryUserIDColumnCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryUserIDColumnCall) Do(f func() database.Column) *MockUserIdentityProviderLinkRepositoryUserIDColumnCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryUserIDColumnCall) DoAndReturn(f func() database.Column) *MockUserIdentityProviderLinkRepositoryUserIDColumnCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UserIDCondition mocks base method. +func (m *MockUserIdentityProviderLinkRepository) UserIDCondition(op database.TextOperation, id string) database.Condition { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UserIDCondition", op, id) + ret0, _ := ret[0].(database.Condition) + return ret0 +} + +// UserIDCondition indicates an expected call of UserIDCondition. +func (mr *MockUserIdentityProviderLinkRepositoryMockRecorder) UserIDCondition(op, id any) *MockUserIdentityProviderLinkRepositoryUserIDConditionCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserIDCondition", reflect.TypeOf((*MockUserIdentityProviderLinkRepository)(nil).UserIDCondition), op, id) + return &MockUserIdentityProviderLinkRepositoryUserIDConditionCall{Call: call} +} + +// MockUserIdentityProviderLinkRepositoryUserIDConditionCall wrap *gomock.Call +type MockUserIdentityProviderLinkRepositoryUserIDConditionCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockUserIdentityProviderLinkRepositoryUserIDConditionCall) Return(arg0 database.Condition) *MockUserIdentityProviderLinkRepositoryUserIDConditionCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockUserIdentityProviderLinkRepositoryUserIDConditionCall) Do(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryUserIDConditionCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockUserIdentityProviderLinkRepositoryUserIDConditionCall) DoAndReturn(f func(database.TextOperation, string) database.Condition) *MockUserIdentityProviderLinkRepositoryUserIDConditionCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/backend/v3/domain/passkeytype_enumer.go b/backend/v3/domain/passkeytype_enumer.go index 7df4950ef..9f255f2e4 100644 --- a/backend/v3/domain/passkeytype_enumer.go +++ b/backend/v3/domain/passkeytype_enumer.go @@ -1,8 +1,10 @@ -// Code generated by "enumer -type PasskeyType -transform lower -trimprefix PasskeyType"; DO NOT EDIT. +// Code generated by "enumer -type PasskeyType -transform lower -trimprefix PasskeyType -json -sql"; DO NOT EDIT. package domain import ( + "database/sql/driver" + "encoding/json" "fmt" "strings" ) @@ -80,3 +82,50 @@ func (i PasskeyType) IsAPasskeyType() bool { } return false } + +// MarshalJSON implements the json.Marshaler interface for PasskeyType +func (i PasskeyType) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for PasskeyType +func (i *PasskeyType) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("PasskeyType should be a string, got %s", data) + } + + var err error + *i, err = PasskeyTypeString(s) + return err +} + +func (i PasskeyType) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *PasskeyType) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of PasskeyType: %[1]T(%[1]v)", value) + } + + val, err := PasskeyTypeString(str) + if err != nil { + return err + } + + *i = val + return nil +} diff --git a/backend/v3/domain/personalaccesstokentype_enumer.go b/backend/v3/domain/personalaccesstokentype_enumer.go deleted file mode 100644 index b31ee5db7..000000000 --- a/backend/v3/domain/personalaccesstokentype_enumer.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by "enumer -type PersonalAccessTokenType -transform lower -trimprefix PersonalAccessTokenType"; DO NOT EDIT. - -package domain - -import ( - "fmt" - "strings" -) - -const _PersonalAccessTokenTypeName = "unspecifiedbearerjwt" - -var _PersonalAccessTokenTypeIndex = [...]uint8{0, 11, 17, 20} - -const _PersonalAccessTokenTypeLowerName = "unspecifiedbearerjwt" - -func (i PersonalAccessTokenType) String() string { - if i >= PersonalAccessTokenType(len(_PersonalAccessTokenTypeIndex)-1) { - return fmt.Sprintf("PersonalAccessTokenType(%d)", i) - } - return _PersonalAccessTokenTypeName[_PersonalAccessTokenTypeIndex[i]:_PersonalAccessTokenTypeIndex[i+1]] -} - -// An "invalid array index" compiler error signifies that the constant values have changed. -// Re-run the stringer command to generate them again. -func _PersonalAccessTokenTypeNoOp() { - var x [1]struct{} - _ = x[PersonalAccessTokenTypeUnspecified-(0)] - _ = x[PersonalAccessTokenTypeBearer-(1)] - _ = x[PersonalAccessTokenTypeJWT-(2)] -} - -var _PersonalAccessTokenTypeValues = []PersonalAccessTokenType{PersonalAccessTokenTypeUnspecified, PersonalAccessTokenTypeBearer, PersonalAccessTokenTypeJWT} - -var _PersonalAccessTokenTypeNameToValueMap = map[string]PersonalAccessTokenType{ - _PersonalAccessTokenTypeName[0:11]: PersonalAccessTokenTypeUnspecified, - _PersonalAccessTokenTypeLowerName[0:11]: PersonalAccessTokenTypeUnspecified, - _PersonalAccessTokenTypeName[11:17]: PersonalAccessTokenTypeBearer, - _PersonalAccessTokenTypeLowerName[11:17]: PersonalAccessTokenTypeBearer, - _PersonalAccessTokenTypeName[17:20]: PersonalAccessTokenTypeJWT, - _PersonalAccessTokenTypeLowerName[17:20]: PersonalAccessTokenTypeJWT, -} - -var _PersonalAccessTokenTypeNames = []string{ - _PersonalAccessTokenTypeName[0:11], - _PersonalAccessTokenTypeName[11:17], - _PersonalAccessTokenTypeName[17:20], -} - -// PersonalAccessTokenTypeString retrieves an enum value from the enum constants string name. -// Throws an error if the param is not part of the enum. -func PersonalAccessTokenTypeString(s string) (PersonalAccessTokenType, error) { - if val, ok := _PersonalAccessTokenTypeNameToValueMap[s]; ok { - return val, nil - } - - if val, ok := _PersonalAccessTokenTypeNameToValueMap[strings.ToLower(s)]; ok { - return val, nil - } - return 0, fmt.Errorf("%s does not belong to PersonalAccessTokenType values", s) -} - -// PersonalAccessTokenTypeValues returns all values of the enum -func PersonalAccessTokenTypeValues() []PersonalAccessTokenType { - return _PersonalAccessTokenTypeValues -} - -// PersonalAccessTokenTypeStrings returns a slice of all String values of the enum -func PersonalAccessTokenTypeStrings() []string { - strs := make([]string, len(_PersonalAccessTokenTypeNames)) - copy(strs, _PersonalAccessTokenTypeNames) - return strs -} - -// IsAPersonalAccessTokenType returns "true" if the value is listed in the enum definition. "false" otherwise -func (i PersonalAccessTokenType) IsAPersonalAccessTokenType() bool { - for _, v := range _PersonalAccessTokenTypeValues { - if i == v { - return true - } - } - return false -} diff --git a/backend/v3/domain/user.go b/backend/v3/domain/user.go index 2417e3822..9b4cdbe6d 100644 --- a/backend/v3/domain/user.go +++ b/backend/v3/domain/user.go @@ -4,6 +4,8 @@ import ( "time" "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/crypto" ) type User struct { @@ -15,48 +17,50 @@ type User struct { CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"` - Machine *MachineUser `json:"machine,omitempty" db:"machine"` - Human *HumanUser `json:"human,omitempty" db:"human"` - Metadata []*UserMetadata `json:"metadata,omitempty" db:"metadata"` + Machine *MachineUser `json:"machine,omitempty" db:"-"` + Human *HumanUser `json:"human,omitempty" db:"-"` + Metadata []*Metadata `json:"metadata,omitempty" db:"metadata"` } type MachineUser struct { - Name string `json:"name,omitempty" db:"name"` - Description string `json:"description,omitempty" db:"description"` - Secret string `json:"secret,omitempty" db:"secret"` - AccessTokenType PersonalAccessTokenType `json:"access_token_type,omitempty" db:"access_token_type"` + Name string `json:"name,omitempty" db:"-"` + Description string `json:"description,omitempty" db:"-"` + Secret string `json:"secret,omitempty" db:"-"` + AccessTokenType AccessTokenType `json:"accessTokenType,omitempty" db:"-"` - PATs []*PersonalAccessToken `json:"pats,omitempty" db:"pats"` - Keys []*MachineKey `json:"keys,omitempty" db:"keys"` + PATs []*PersonalAccessToken `json:"pats,omitempty" db:"-"` + Keys []*MachineKey `json:"keys,omitempty" db:"-"` } type HumanUser struct { - FirstName string `json:"firstName,omitempty" db:"first_name"` - LastName string `json:"lastName,omitempty" db:"last_name"` - Nickname string `json:"nickname,omitempty" db:"nickname"` - DisplayName string `json:"displayName,omitempty" db:"display_name"` - PreferredLanguage language.Tag `json:"preferredLanguage,omitzero" db:"preferred_language"` - Gender HumanGender `json:"gender,omitempty" db:"gender"` - AvatarKey string `json:"avatarKey,omitempty" db:"avatar_key"` + FirstName string `json:"firstName,omitempty" db:"-"` + LastName string `json:"lastName,omitempty" db:"-"` + Nickname string `json:"nickname,omitempty" db:"-"` + DisplayName string `json:"displayName,omitempty" db:"-"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitzero" db:"-"` + Gender HumanGender `json:"gender,omitempty" db:"-"` + AvatarKey string `json:"avatarKey,omitempty" db:"-"` - MultifactorInitializationSkippedAt time.Time `json:"multifactorInitializationSkippedAt,omitzero" db:"multifactor_initialization_skipped_at"` + MultifactorInitializationSkippedAt time.Time `json:"multifactorInitializationSkippedAt,omitzero" db:"-"` - Email HumanEmail `json:"email,omitzero" db:"email"` - Phone *HumanPhone `json:"phone,omitempty" db:"phone"` - Passkeys []*Passkey `json:"passkeys,omitempty" db:"passkeys"` - Password HumanPassword `json:"password,omitzero" db:"password"` - TOTP HumanTOTP `json:"totp,omitzero" db:"totp"` + Email HumanEmail `json:"email,omitzero" db:"-"` + Phone *HumanPhone `json:"phone,omitempty" db:"-"` + Passkeys []*Passkey `json:"passkeys,omitempty" db:"-"` + Password HumanPassword `json:"password,omitzero" db:"-"` + TOTP *HumanTOTP `json:"totp,omitempty" db:"-"` - IdentityProviderLinks []*IdentityProviderLink `json:"identityProviderLinks,omitempty" db:"identity_provider_links"` + IdentityProviderLinks []*IdentityProviderLink `json:"identityProviderLinks,omitempty" db:"-"` + + Invite *HumanInvite `json:"invite,omitempty" db:"-"` // Verifications are used if the Login does something on behalf of the user that requires verification // e.g. starting passkey registration - Verifications []*Verification `db:"verifications" json:"verifications,omitempty"` + Verifications []*Verification `json:"verifications,omitempty" db:"-"` } type UserMetadata struct { Metadata - UserID string `json:"userId,omitempty" db:"user_id"` + UserID string `json:"userId,omitempty" db:"-"` } //go:generate enumer -type UserState -transform lower -trimprefix UserState -sql @@ -83,81 +87,84 @@ const ( ) type HumanPassword struct { - // Password is the hashed password - Password string `json:"-" db:"password"` + // Hash is the hashed password + Hash string `json:"hash" db:"-"` // IsChangeRequired indicates if the user must change their password - IsChangeRequired bool `json:"isChangeRequired,omitempty" db:"is_change_required"` - // VerifiedAt is the time when the current password was verified - VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"verified_at"` - // Unverified is the verification data for setting a new password + IsChangeRequired bool `json:"isChangeRequired,omitempty" db:"-"` + // ChangedAt is the time when the current password was last updated + ChangedAt time.Time `json:"changedAt,omitzero" db:"-"` + // PendingVerification is the verification data for setting a new password // If nil, no password change is in progress - Unverified *Verification `json:"-"` + PendingVerification *Verification `json:"pendingVerification,omitempty" db:"-"` + // LastSuccessfullyCheckedAt is the time when the password was last successfully checked + LastSuccessfullyCheckedAt *time.Time `json:"lastSuccessfullyCheckedAt,omitzero" db:"-"` // FailedAttempts is the number of consecutive failed password attempts // It is reset to 0 on successful verification - FailedAttempts uint8 `json:"failedAttempts,omitempty" db:"failed_attempts"` + FailedAttempts uint8 `json:"failedAttempts,omitempty" db:"-"` } type HumanEmail struct { - Address string `json:"address" db:"email"` - VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"verified_at"` - OTP OTP `json:"-"` - // Unverified is the verification data for setting a new email + Address string `json:"address" db:"-"` + UnverifiedAddress string `json:"unverifiedAddress,omitempty" db:"-"` + VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"-"` + OTP OTP `json:"otp" db:"-"` + // PendingVerification is the verification data for UnverifiedAddress // If nil, no email change is in progress - Unverified *Verification `json:"-"` + PendingVerification *Verification `json:"pendingVerification,omitempty" db:"-"` } type HumanPhone struct { - Number string `json:"number" db:"phone"` - VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"verified_at"` - OTP OTP `json:"-"` - // Unverified is the verification data for setting a new phone number + Number string `json:"number" db:"-"` + UnverifiedNumber string `json:"unverifiedNumber,omitempty" db:"-"` + VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"-"` + OTP OTP `json:"otp,omitzero" db:"-"` + // PendingVerification is the verification data for setting a new phone number // If nil, no phone change is in progress - Unverified *Verification `json:"-"` + PendingVerification *Verification `json:"pendingVerification,omitempty" db:"-"` } type HumanTOTP struct { - VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"verified_at"` + VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"-"` + Secret *crypto.CryptoValue `json:"secret,omitempty" db:"-"` + // LastSuccessfullyCheckedAt is the time when the TOTP was last successfully checked + LastSuccessfullyCheckedAt *time.Time `json:"lastSuccessfullyCheckedAt,omitzero" db:"-"` - LastSuccessfullyCheckedAt time.Time `json:"lastSuccessfullyCheckedAt,omitzero" db:"last_successfully_checked_at"` - // Check is the verified secret - Check *Check `json:"-"` - // Unverified is the unverified secret - Unverified *Verification `json:"-"` + // FailedAttempts is the number of consecutive failed password attempts + // It is reset to 0 on successful verification + FailedAttempts uint8 `json:"failedAttempts,omitempty" db:"-"` } type OTP struct { - EnabledAt time.Time `json:"enabledAt,omitzero" db:"enabled_at"` - LastSuccessfullyCheckedAt time.Time `json:"lastSuccessfullyCheckedAt,omitzero" db:"last_successfully_checked_at"` - // Check is the currently active OTP check - // If nil, no OTP check is active - Check *Check `json:"-"` + EnabledAt time.Time `json:"enabledAt,omitzero" db:"-"` + // LastSuccessfullyCheckedAt is the time when the OTP was last successfully checked + LastSuccessfullyCheckedAt *time.Time `json:"lastSuccessfullyCheckedAt,omitzero" db:"-"` + // FailedAttempts is the number of consecutive failed password attempts + // It is reset to 0 on successful verification + FailedAttempts uint8 `json:"failedAttempts,omitempty" db:"-"` } type PersonalAccessToken struct { - ID string `json:"id" db:"id"` - CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` - ExpiresAt time.Time `json:"expiresAt,omitzero" db:"expires_at"` - Type PersonalAccessTokenType `json:"type" db:"type"` - PublicKey string `json:"-" db:"public_key"` - Scopes []string `json:"scopes" db:"scopes"` + ID string `json:"id" db:"-"` + CreatedAt time.Time `json:"createdAt,omitzero" db:"-"` + ExpiresAt time.Time `json:"expiresAt,omitzero" db:"-"` + Scopes []string `json:"scopes" db:"-"` } -//go:generate enumer -type PersonalAccessTokenType -transform lower -trimprefix PersonalAccessTokenType - -type PersonalAccessTokenType uint8 +//go:generate enumer -type AccessTokenType -transform lower -trimprefix AccessTokenType +type AccessTokenType uint8 const ( - PersonalAccessTokenTypeUnspecified PersonalAccessTokenType = iota - PersonalAccessTokenTypeBearer - PersonalAccessTokenTypeJWT + AccessTokenTypeUnspecified AccessTokenType = iota + AccessTokenTypeBearer + AccessTokenTypeJWT ) type MachineKey struct { - ID string `json:"id" db:"id"` - PublicKey []byte `json:"-" db:"public_key"` - CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` - ExpiresAt time.Time `json:"expiresAt,omitzero" db:"expires_at"` - Type MachineKeyType `json:"type" db:"type"` + ID string `json:"id" db:"-"` + PublicKey []byte `json:"publicKey" db:"-"` + CreatedAt time.Time `json:"createdAt,omitzero" db:"-"` + ExpiresAt time.Time `json:"expiresAt,omitzero" db:"-"` + Type MachineKeyType `json:"type" db:"-"` } //go:generate enumer -type MachineKeyType -transform lower -trimprefix MachineKeyType @@ -165,35 +172,34 @@ type MachineKey struct { type MachineKeyType uint8 const ( - MachineKeyTypeUnspecified MachineKeyType = iota + MachineKeyTypeNone MachineKeyType = iota MachineKeyTypeJSON - MachineKeyTypeNone ) type Passkey struct { - ID string `json:"id" db:"id"` - KeyID []byte `json:"-" db:"key_id"` - Name string `json:"name" db:"name"` - SignCount uint32 `json:"-" db:"sign_count"` - PublicKey []byte `json:"-" db:"public_key"` - AttestationType string `json:"attestationType" db:"attestation_type"` - AuthenticatorAttestationGUID []byte `json:"aaGuid" db:"authenticator_attestation_guid"` - Type PasskeyType `json:"type" db:"type"` + ID string `json:"id" db:"-"` + KeyID []byte `json:"keyId" db:"-"` + Name string `json:"name" db:"-"` + SignCount uint32 `json:"signCount" db:"-"` + PublicKey []byte `json:"publicKey" db:"-"` + AttestationType string `json:"attestationType" db:"-"` + AuthenticatorAttestationGUID []byte `json:"aaGuid" db:"-"` + Type PasskeyType `json:"type" db:"-"` - CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` - UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"` - VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"verified_at"` + CreatedAt time.Time `json:"createdAt,omitzero" db:"-"` + UpdatedAt time.Time `json:"updatedAt,omitzero" db:"-"` + VerifiedAt time.Time `json:"verifiedAt,omitzero" db:"-"` // Challenge is used during device registration - Challenge []byte `json:"-" db:"challenge"` + Challenge []byte `json:"challenge" db:"-"` // RelyingPartyID is used during device registration - RelyingPartyID string `json:"-" db:"relying_party_id"` + RelyingPartyID string `json:"rpId" db:"-"` // Initialization is used during user registration - Initialization *Verification `json:"-" db:"initialization"` + Initialization *Verification `json:"-" db:"-"` } -//go:generate enumer -type PasskeyType -transform lower -trimprefix PasskeyType +//go:generate enumer -type PasskeyType -transform lower -trimprefix PasskeyType -json -sql type PasskeyType uint8 @@ -204,12 +210,12 @@ const ( ) type IdentityProviderLink struct { - ProviderID string `json:"providerId" db:"provider_id"` - ProvidedUserID string `json:"providedUserId" db:"provided_user_id"` - ProvidedUsername string `json:"providedUsername" db:"provided_username"` + ProviderID string `json:"providerId" db:"-"` + ProvidedUserID string `json:"providedUserId" db:"-"` + ProvidedUsername string `json:"providedUsername" db:"-"` - CreatedAt time.Time `json:"createdAt,omitzero" db:"created_at"` - UpdatedAt time.Time `json:"updatedAt,omitzero" db:"updated_at"` + CreatedAt time.Time `json:"createdAt,omitzero" db:"-"` + UpdatedAt time.Time `json:"updatedAt,omitzero" db:"-"` } //go:generate enumer -type UserType -transform lower -trimprefix UserType @@ -221,3 +227,12 @@ const ( UserTypeHuman UserTypeMachine ) + +type HumanInvite struct { + AcceptedAt time.Time `json:"acceptedAt,omitzero" db:"-"` + // PendingVerification is the verification data for UnverifiedAddress + // If nil, no email change is in progress + PendingVerification *Verification `json:"pendingVerification,omitempty" db:"-"` + // FailedAttempts is the number of consecutive failed invite attempts + FailedAttempts uint8 `json:"failedAttempts,omitempty" db:"-"` +} diff --git a/backend/v3/domain/user_identity_provider_link.go b/backend/v3/domain/user_identity_provider_link.go new file mode 100644 index 000000000..e1af25da9 --- /dev/null +++ b/backend/v3/domain/user_identity_provider_link.go @@ -0,0 +1,60 @@ +package domain + +import ( + "context" + "time" + + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type UserIdentityProviderLink struct { + InstanceID string `json:"instanceID" db:"instance_id"` + UserID string `json:"userID" db:"user_id"` + IdentityProviderID string `json:"identityProviderID" db:"identity_provider_id"` + ProvidedID string `json:"providedID" db:"provided_id"` + ProvidedUsername string `json:"providedUsername" db:"provided_username"` + + CreatedAt time.Time `json:"createdAt,omitempty" db:"created_at"` + UpdatedAt time.Time `json:"updatedAt,omitempty" db:"updated_at"` +} + +//go:generate mockgen -typed -package domainmock -destination ./mock/user_identity_provider_link.mock.go . UserIdentityProviderLinkRepository +type UserIdentityProviderLinkRepository interface { + Repository + userIdentityProviderLinkColumns + userIdentityProviderLinkConditions + userIdentityProviderLinkChanges + + Get(ctx context.Context, client database.QueryExecutor, condition database.Condition, opts ...database.QueryOption) (*UserIdentityProviderLink, error) + List(ctx context.Context, client database.QueryExecutor, condition database.Condition, opts ...database.QueryOption) ([]*UserIdentityProviderLink, error) + + Create(ctx context.Context, client database.QueryExecutor, link *UserIdentityProviderLink) error + Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) + Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) +} + +type userIdentityProviderLinkColumns interface { + InstanceIDColumn() database.Column + UserIDColumn() database.Column + IdentityProviderIDColumn() database.Column + ProvidedIDColumn() database.Column + ProvidedUsernameColumn() database.Column + + CreatedAtColumn() database.Column + UpdatedAtColumn() database.Column +} + +type userIdentityProviderLinkConditions interface { + PrimaryKeyCondition(instanceID, idpID, providedID string) database.Condition + InstanceIDCondition(op database.TextOperation, id string) database.Condition + UserIDCondition(op database.TextOperation, id string) database.Condition + IdentityProviderIDCondition(op database.TextOperation, id string) database.Condition + ProvidedIDCondition(op database.TextOperation, id string) database.Condition + ProvidedUsernameCondition(op database.TextOperation, username string) database.Condition +} + +type userIdentityProviderLinkChanges interface { + SetProvidedUsername(username string) database.Change + SetUpdatedAt(updatedAt time.Time) database.Change + SetProvidedUserID(providedID string) database.Change +} diff --git a/backend/v3/domain/user_repo.go b/backend/v3/domain/user_repo.go index 33077e758..5086f6490 100644 --- a/backend/v3/domain/user_repo.go +++ b/backend/v3/domain/user_repo.go @@ -7,6 +7,7 @@ import ( "golang.org/x/text/language" "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/internal/crypto" ) //go:generate mockgen -typed -package domainmock -destination ./mock/user.mock.go . UserRepository @@ -22,13 +23,6 @@ type UserRepository interface { Human() HumanUserRepository Machine() MachineUserRepository - LoadMetadata() UserRepository - LoadPasskeys() UserRepository - LoadIdentityProviderLinks() UserRepository - LoadVerifications() UserRepository - LoadPATs() UserRepository - LoadKeys() UserRepository - userConditions userChanges userColumns @@ -37,18 +31,18 @@ type UserRepository interface { type userConditions interface { PrimaryKeyCondition(instanceID, userID string) database.Condition InstanceIDCondition(instanceID string) database.Condition - OrgIDCondition(orgID string) database.Condition + OrganizationIDCondition(orgID string) database.Condition IDCondition(userID string) database.Condition UsernameCondition(op database.TextOperation, username string) database.Condition StateCondition(state UserState) database.Condition TypeCondition(userType UserType) database.Condition LoginNameCondition(op database.TextOperation, loginName string) database.Condition - userMetadataConditions + ExistsMetadata(condition database.Condition) database.Condition + MetadataConditions() UserMetadataConditions } -type userMetadataConditions interface { - ExistsMetadata(condition database.Condition) database.Condition +type UserMetadataConditions interface { MetadataKeyCondition(op database.TextOperation, key string) database.Condition MetadataValueCondition(op database.BytesOperation, value []byte) database.Condition } @@ -62,8 +56,8 @@ type userChanges interface { // SetUpdatedAt sets the updated at field // This is used to replay events SetUpdatedAt(updatedAt time.Time) database.Change - // AddMetadata adds metadata for the user - AddMetadata(metadata ...*Metadata) database.Change + // SetMetadata adds metadata for the user + SetMetadata(metadata ...*Metadata) database.Change // RemoveMetadata removes the metadata for the user // Use one of the following conditions to specify which metadata to remove: // - [userMetadataConditions.MetadataKeyCondition] @@ -74,6 +68,8 @@ type userChanges interface { type userColumns interface { PrimaryKeyColumns() []database.Column + InstanceIDColumn() database.Column + IDColumn() database.Column UsernameColumn() database.Column StateColumn() database.Column TypeColumn() database.Column @@ -98,18 +94,32 @@ type humanConditions interface { NicknameCondition(op database.TextOperation, nickname string) database.Condition DisplayNameCondition(op database.TextOperation, displayName string) database.Condition PreferredLanguageCondition(lang language.Tag) database.Condition - EmailCondition(op database.TextOperation, email string) database.Condition - PhoneCondition(op database.TextOperation, phone string) database.Condition - PasskeyIDCondition(passkeyID string) database.Condition - IdentityProviderIDCondition(idpID string) database.Condition - ProvidedUserIDCondition(providedUserID string) database.Condition - ProvidedUsernameCondition(username string) database.Condition - humanPasskeyConditions + // EmailCondition searches for the given email in both verified and unverified email columns + EmailCondition(op database.TextOperation, email string) database.Condition + // UnverifiedEmailCondition searches for the given email in the unverified email column + UnverifiedEmailCondition(op database.TextOperation, email string) database.Condition + // VerifiedEmailCondition searches for the given email in the verified email column + VerifiedEmailCondition(op database.TextOperation, email string) database.Condition + + // PhoneCondition searches for the given phone number in both verified and unverified phone number columns + PhoneCondition(op database.TextOperation, phone string) database.Condition + // UnverifiedPhoneCondition searches for the given phone number in the unverified phone number column + UnverifiedPhoneCondition(op database.TextOperation, phone string) database.Condition + // VerifiedPhoneCondition searches for the given phone number in the verified phone number column + VerifiedPhoneCondition(op database.TextOperation, phone string) database.Condition + + ExistsPasskey(condition database.Condition) database.Condition + PasskeyConditions() HumanPasskeyConditions + // ExistsVerification(condition database.Condition) database.Condition + // VerificationConditions() HumanVerificationConditions + ExistsIdentityProviderLink(condition database.Condition) database.Condition + IdentityProviderLinkConditions() HumanIdentityProviderLinkConditions } type humanChanges interface { userChanges + // SetFirstName sets the first name field SetFirstName(firstName string) database.Change // SetLastName sets the last name field @@ -127,99 +137,148 @@ type humanChanges interface { // SetAvatarKey sets the avatar key field // If avatarKey is nil, it will be set to NULL in the database SetAvatarKey(avatarKey *string) database.Change - // SetMultifactorInitializationSkippedAt sets the multifactor initialization skipped at field - SetMultifactorInitializationSkippedAt(skippedAt time.Time) database.Change - - // SetPassword sets the password based on the verification - SetPassword(verification VerificationType) database.Change - // SetPasswordChangeRequired sets whether a password change is required - SetPasswordChangeRequired(required bool) database.Change - // CheckPassword sets the password check based on the check type - // * [CheckTypeFailed] increments failed attempts - // * [CheckTypeSucceeded] to mark the check as succeeded, removes the check and updates verified at time - CheckPassword(check PasswordCheckType) database.Change + // SkipMultifactorInitializationAt sets the multifactor initialization skipped at field + SkipMultifactorInitializationAt(skippedAt time.Time) database.Change + // SkipMultifactorInitializationAt sets the multifactor initialization skipped at field + SkipMultifactorInitialization() database.Change // SetVerification sets the verification based on the verification type - SetVerification(id string, verification VerificationType) database.Change + SetVerification(verification VerificationType) database.Change + humanPasswordChanges humanEmailChanges humanPhoneChanges humanTOTPChanges identityProviderLinkChanges humanPasskeyChanges + inviteChanges +} + +type humanPasswordChanges interface { + // SetPassword overwrites the password field. + // It resets the password verification and the last successful password check time. + SetPassword(hash string) database.Change + // SetResetPassword handles the password reset process based on the verification type: + // * [VerificationTypeInit]: to initialize password reset, a verification will be created. The current password remains unchanged + // * [VerificationTypeSucceeded]: to mark the password as reset and remove the verification. The current password remains unchanged use [humanPasswordChanges.SetPassword] to set the new password. + // * [VerificationTypeUpdate]: to update the existing password reset verification. If a new code is requested, use [VerificationTypeInit]. + // * [VerificationTypeFailed]: to increment the failed attempts of the verification. + SetResetPasswordVerification(verification VerificationType) database.Change + // SetPasswordChangeRequired sets whether a password change is required + SetPasswordChangeRequired(required bool) database.Change + // SetLastSuccessfulPasswordCheck sets the last successful password check time + // If checkedAt is zero, it will be set to NOW() + SetLastSuccessfulPasswordCheck(checkedAt time.Time) database.Change + // IncrementPasswordFailedAttempts increments the password failed attempts + IncrementPasswordFailedAttempts() database.Change + // ResetPasswordFailedAttempts resets the password failed attempts + ResetPasswordFailedAttempts() database.Change } type humanEmailChanges interface { - // SetEmail sets the email based on the verification - // * [VerificationTypeInit] to initialize email verification, previously verified email remains verified - // * [VerificationTypeVerified] to mark email as verified, a verification must exist - // * [VerificationTypeUpdate] to update email verification, a verification must exist (e.g. resend code) - // * [VerificationTypeSkipped] to skip email verification, existing verification is removed (e.g. admin set email) - SetEmail(verification VerificationType) database.Change - // CheckEmailOTP sets the OTP Email based on the check - // * [CheckTypeInit] to initialize a new check, previous check is overwritten - // * [CheckTypeFailed] increments failed attempts - // * [CheckTypeSucceeded] to mark the check as succeeded, removes the check and updates verified at time - CheckEmailOTP(check CheckType) database.Change - // EnableEmailOTPAt enables the OTP Email + // SetEmail sets the verified email address + SetEmail(address string) database.Change + // SetUnverifiedEmail sets the unverified email address + // Make sure to add [humanEmailChanges.SetEmailVerification] to the changes list. + SetUnverifiedEmail(address string) database.Change + // SetEmailVerification sets the email verification based on the verification + // * [VerificationTypeInit]: to overwrite a email verification, the previously verified email address remains verified. + // * [VerificationTypeSucceeded]: to mark the unverified email address as verified and remove the verification. + // * [VerificationTypeUpdate]: to update the existing email verification. If a new code is requested, use [VerificationTypeInit]. + // * [VerificationTypeFailed]: to increment the failed attempts of the verification. + SetEmailVerification(verification VerificationType) database.Change + // EnableEmailOTPAt enables the email OTP // If enabledAt is zero, it will be set to NOW() EnableEmailOTPAt(enabledAt time.Time) database.Change // EnableEmailOTP sets the enabled at time to NOW() EnableEmailOTP() database.Change // DisableEmailOTP clears the enabled at time DisableEmailOTP() database.Change + // SetLastSuccessfulEmailOTPCheck sets the last successful email OTP check time + // If checkedAt is zero, it will be set to NOW() + // It resets the failed attempts + SetLastSuccessfulEmailOTPCheck(checkedAt time.Time) database.Change + // IncrementEmailOTPFailedAttempts increments the email OTP failed attempts + IncrementEmailOTPFailedAttempts() database.Change + // ResetEmailOTPFailedAttempts resets the email OTP failed attempts + ResetEmailOTPFailedAttempts() database.Change } type humanPhoneChanges interface { - // SetPhone sets the phone based on the verification - // * [VerificationTypeInit] to initialize phone verification, previously verified phone remains verified - // * [VerificationTypeVerified] to mark phone as verified, a verification must exist - // * [VerificationTypeUpdate] to update phone verification, a verification must exist (e.g. resend code) - // * [VerificationTypeSkipped] to skip phone verification, existing verification is removed (e.g. admin set phone) - SetPhone(verification VerificationType) database.Change + // SetPhone sets the verified phone number + SetPhone(number string) database.Change + // SetUnverifiedPhone sets the unverified phone number + // Make sure to add [humanPhoneChanges.SetPhoneVerification] to the changes list. + SetUnverifiedPhone(number string) database.Change + // SetPhoneVerification sets the phone verification based on the verification + // * [VerificationTypeInit]: to overwrite a phone verification, the previously verified phone number remains verified. + // * [VerificationTypeSucceeded]: to mark the unverified phone number as verified and remove the verification. + // * [VerificationTypeUpdate]: to update the existing phone verification. If a new code is requested, use [VerificationTypeInit]. + // * [VerificationTypeFailed]: to increment the failed attempts of the verification. + SetPhoneVerification(verification VerificationType) database.Change // RemovePhone removes the phone number RemovePhone() database.Change - // CheckSMSOTP sets the OTP SMS based on the check - // * [CheckTypeInit] to initialize a new check, previous check is overwritten - // * [CheckTypeFailed] increments failed attempts - // * [CheckTypeSucceeded] to mark the check as succeeded, removes the check and updates verified at time - CheckSMSOTP(check CheckType) database.Change - // EnableSMSOTPAt enables the OTP SMS + // EnableSMSOTPAt enables the SMS OTP // If enabledAt is zero, it will be set to NOW() EnableSMSOTPAt(enabledAt time.Time) database.Change // EnableSMSOTP sets the enabled at time to NOW() EnableSMSOTP() database.Change // DisableSMSOTP clears the enabled at time DisableSMSOTP() database.Change + // SetLastSuccessfulSMSOTPCheck sets the last successful SMS OTP check time + // If checkedAt is zero, it will be set to NOW() + // It resets the failed attempts + SetLastSuccessfulSMSOTPCheck(checkedAt time.Time) database.Change + // IncrementSMSOTPFailedAttempts increments the SMS OTP failed attempts + IncrementSMSOTPFailedAttempts() database.Change + // ResetSMSOTPFailedAttempts resets the SMS OTP failed attempts + ResetSMSOTPFailedAttempts() database.Change } type humanTOTPChanges interface { - // SetTOTP changes the TOTP secret based on the verification - // * [VerificationTypeInit] to initialize totp verification, previously verified totp remains verified - // * [VerificationTypeVerified] to mark totp as verified, a verification must exist - // * [VerificationTypeUpdate] to update totp verification, a verification must exist (e.g. resend code) - // * [VerificationTypeSkipped] to skip totp verification, existing verification is removed (e.g. admin set totp) - SetTOTP(verification VerificationType) database.Change - // SetTOTPCheck sets the TOTP check based on the check type - // * [CheckTypeFailed] increments failed attempts - // * [CheckTypeSucceeded] to mark the check as succeeded, removes the check and updates verified at time - // * [CheckTypeInit] is not allowed for TOTP - CheckTOTP(check CheckType) database.Change + // SetTOTP changes the TOTP secret and verification timestamp + SetTOTPSecret(secret *crypto.CryptoValue) database.Change + // SetTOTPVerifiedAt sets the TOTP verified at time + // If verifiedAt is zero, it will be set to NOW() + SetTOTPVerifiedAt(verifiedAt time.Time) database.Change // RemoveTOTP removes the TOTP RemoveTOTP() database.Change + // SetLastSuccessfulTOTPCheck sets the last successful TOTP check time + // If checkedAt is zero, it will be set to NOW() + // It resets the failed attempts + SetLastSuccessfulTOTPCheck(checkedAt time.Time) database.Change + // IncrementTOTPFailedAttempts increments the TOTP failed attempts + IncrementTOTPFailedAttempts() database.Change + // ResetTOTPFailedAttempts resets the TOTP failed attempts + ResetTOTPFailedAttempts() database.Change } type identityProviderLinkChanges interface { // AddIdentityProviderLink adds or updates an identity provider link AddIdentityProviderLink(link *IdentityProviderLink) database.Change - UpdateIdentityProviderLink(changes ...database.Change) database.Change + UpdateIdentityProviderLink(condition database.Condition, changes ...database.Change) database.Change // RemoveIdentityProviderLink removes an identity provider link based on the condition RemoveIdentityProviderLink(providerID, providedUserID string) database.Change // SetIdentityProviderLinkUsername sets the username for an identity provider link - SetIdentityProviderLinkUsername(providerID, providedUserID, username string) database.Change + SetIdentityProviderLinkUsername(username string) database.Change // SetIdentityProviderLinkProvidedID sets the provided user ID for an identity provider link - SetIdentityProviderLinkProvidedID(providerID, currentProvidedUserID, newProvidedUserID string) database.Change + SetIdentityProviderLinkProvidedID(providedUserID string) database.Change +} + +type inviteChanges interface { + // SetInviteVerification sets the invite verification based on the verification type: + // * [VerificationTypeInit]: to initialize invite verification, a verification will be created. The current accepted at time remains unchanged + // * [VerificationTypeSucceeded]: to mark the invite as accepted and remove the verification. The current accepted at time remains unchanged use [inviteChanges.AcceptInvite] to set the accepted at time. + // * [VerificationTypeUpdate]: to update the existing invite verification. If a new code is requested, use [VerificationTypeInit]. + // * [VerificationTypeFailed]: to increment the failed attempts of the verification. + SetInviteVerification(verification VerificationType) database.Change +} + +type HumanIdentityProviderLinkConditions interface { + ProviderIDCondition(providerID string) database.Condition + ProvidedUserIDCondition(providedUserID string) database.Condition + ProvidedUsernameCondition(op database.TextOperation, username string) database.Condition } type humanColumns interface { @@ -256,7 +315,7 @@ type machineChanges interface { // nil will set the secret to NULL in the database SetSecret(secret *string) database.Change // SetAccessTokenType sets the personal access token type field - SetAccessTokenType(tokenType PersonalAccessTokenType) database.Change + SetAccessTokenType(tokenType AccessTokenType) database.Change // AddKey adds a key for the service account AddKey(key *MachineKey) database.Change @@ -273,11 +332,11 @@ type machineColumns interface { userColumns } -type humanPasskeyConditions interface { - PasskeyIDCondition(passkeyID string) database.Condition - PasskeyKeyIDCondition(keyID string) database.Condition - PasskeyChallengeCondition(challenge string) database.Condition - PasskeyTypeCondition(passkeyType PasskeyType) database.Condition +type HumanPasskeyConditions interface { + IDCondition(passkeyID string) database.Condition + KeyIDCondition(keyID string) database.Condition + ChallengeCondition(challenge []byte) database.Condition + TypeCondition(passkeyType PasskeyType) database.Condition } type humanPasskeyChanges interface { diff --git a/backend/v3/domain/verification.go b/backend/v3/domain/verification.go index d331a13da..8fa7cd49d 100644 --- a/backend/v3/domain/verification.go +++ b/backend/v3/domain/verification.go @@ -1,13 +1,18 @@ package domain -import "time" +import ( + "time" + + "github.com/zitadel/zitadel/internal/crypto" +) type Verification struct { - Value *string `db:"value"` - Code []byte `db:"code"` - ExpiresAt *time.Time `db:"expires_at"` - FailedAttempts uint8 `db:"failed_attempts"` - VerifiedAt time.Time `db:"verified_at"` + ID string `json:"id" db:"id"` + Code *crypto.CryptoValue `json:"code" db:"code"` + CreatedAt time.Time `json:"createdAt" db:"created_at"` + UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` + ExpiresAt *time.Time `json:"expiresAt" db:"expires_at"` + FailedAttempts uint8 `json:"failedAttempts" db:"failed_attempts"` } type VerificationType interface { @@ -16,15 +21,16 @@ type VerificationType interface { // VerificationTypeInit indicates that an object which needs verification is created. type VerificationTypeInit struct { + // ID is the ID of the verification. + // The id must be set if the the object can have multiple verifications. + ID *string // CreatedAt is the time when the verification was created. // If zero, the current time will be used. CreatedAt time.Time // Expiry is the duration after which the verification expires. Expiry *time.Duration // Code is the code to be used for verification. - Code []byte - // Value is the value to be set after successful verification. - Value *string + Code *crypto.CryptoValue } // isVerificationType implements [VerificationType]. @@ -32,24 +38,31 @@ func (v *VerificationTypeInit) isVerificationType() {} var _ VerificationType = (*VerificationTypeInit)(nil) -// VerificationTypeVerified indicates that the verification was successful. +// VerificationTypeSucceeded indicates that the verification was successful. // If VerifiedAt is zero, the current time will be used. -// If the Value of the verification is present, it is used as the new value to be set. -type VerificationTypeVerified struct { +type VerificationTypeSucceeded struct { + // ID is the ID of the verification. + // The id must be set if the the object can have multiple verifications. + ID *string + // If zero, the current time will be used. VerifiedAt time.Time } // isVerificationType implements [VerificationType]. -func (v *VerificationTypeVerified) isVerificationType() {} +func (v *VerificationTypeSucceeded) isVerificationType() {} -var _ VerificationType = (*VerificationTypeVerified)(nil) +var _ VerificationType = (*VerificationTypeSucceeded)(nil) // VerificationTypeUpdate updates an existing verification. // Non-nil fields get updated. type VerificationTypeUpdate struct { - Code []byte - Value *string + // ID is the ID of the verification. + // The id must be set if the the object can have multiple verifications. + ID *string + Code *crypto.CryptoValue Expiry *time.Duration + // If zero, the current time will be used. + UpdatedAt time.Time } // isVerificationType implements [VerificationType]. @@ -58,10 +71,12 @@ func (v *VerificationTypeUpdate) isVerificationType() {} var _ VerificationType = (*VerificationTypeUpdate)(nil) // VerificationTypeSkipped indicates that the verification was skipped. -// If Value is present, it is used as the new value to be set. type VerificationTypeSkipped struct { - VerifiedAt time.Time - Value *string + // ID is the ID of the verification. + // The id must be set if the the object can have multiple verifications. + ID *string + // If zero, the current time will be used. + SkippedAt time.Time } // isVerificationType implements [VerificationType]. @@ -70,6 +85,10 @@ func (v *VerificationTypeSkipped) isVerificationType() {} var _ VerificationType = (*VerificationTypeSkipped)(nil) type VerificationTypeFailed struct { + // ID is the ID of the verification. + // The id must be set if the the object can have multiple verifications. + ID *string + // If zero, the current time will be used. FailedAt time.Time } diff --git a/backend/v3/storage/database/change.go b/backend/v3/storage/database/change.go index ba9129a5d..a5427b938 100644 --- a/backend/v3/storage/database/change.go +++ b/backend/v3/storage/database/change.go @@ -5,12 +5,15 @@ import ( "slices" "go.uber.org/mock/gomock" + + "github.com/zitadel/zitadel/backend/v3/instrumentation/logging" ) // Change represents a change to a column in a database table. // Its written in the SET clause of an UPDATE statement. type Change interface { gomock.Matcher + argWriter // Write writes the change to the given statement builder. Write(builder *StatementBuilder) error // IsOnColumn checks if the change is on the given column. @@ -44,6 +47,25 @@ func (c *change[V]) String() string { return "database.change" } +// WriteArg implements [Change]. +func (c change[V]) WriteArg(builder *StatementBuilder) { + err := c.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) +} + +// Write implements [Change]. +func (c change[V]) Write(builder *StatementBuilder) error { + c.column.WriteUnqualified(builder) + builder.WriteString(" = ") + builder.WriteArg(c.value) + return nil +} + +// IsOnColumn implements [Change]. +func (c change[V]) IsOnColumn(col Column) bool { + return c.column.Equals(col) +} + var ( _ Change = (*change[string])(nil) _ gomock.Matcher = (*change[string])(nil) @@ -67,22 +89,9 @@ func NewChangePtr[V Value](col Column, value *V) Change { return NewChange(col, *value) } -// Write implements [Change]. -func (c change[V]) Write(builder *StatementBuilder) error { - c.column.WriteUnqualified(builder) - builder.WriteString(" = ") - builder.WriteArg(c.value) - return nil -} - -// IsOnColumn implements [Change]. -func (c change[V]) IsOnColumn(col Column) bool { - return c.column.Equals(col) -} - type Changes []Change -func NewChanges(cols ...Change) Change { +func NewChanges(cols ...Change) Changes { return Changes(cols) } @@ -152,6 +161,12 @@ func (c Changes) NoChange() bool { return true } +// WriteArg implements [Change]. +func (c Changes) WriteArg(builder *StatementBuilder) { + err := c.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) +} + var _ Change = Changes(nil) func NewChangeToNull(col Column) Change { @@ -194,26 +209,34 @@ func (c *changeToColumn) Write(builder *StatementBuilder) error { return nil } -var _ Change = (*changeToColumn)(nil) - -type incrementColumnChange struct { - column Column +// WriteArg implements [Change]. +func (c *changeToColumn) WriteArg(builder *StatementBuilder) { + err := c.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) } -func NewIncrementColumnChange(col Column) Change { - return &incrementColumnChange{ +var _ Change = (*changeToColumn)(nil) + +type incrementColumnChange[V Value] struct { + column Column + value V +} + +func NewIncrementColumnChange[V Value](col Column, value V) *incrementColumnChange[V] { + return &incrementColumnChange[V]{ column: col, + value: value, } } // IsOnColumn implements [Change]. -func (i *incrementColumnChange) IsOnColumn(col Column) bool { +func (i incrementColumnChange[V]) IsOnColumn(col Column) bool { return i.column.Equals(col) } // Matches implements [Change]. -func (i *incrementColumnChange) Matches(x any) bool { - toMatch, ok := x.(*incrementColumnChange) +func (i *incrementColumnChange[V]) Matches(x any) bool { + toMatch, ok := x.(*incrementColumnChange[V]) if !ok { return false } @@ -221,20 +244,26 @@ func (i *incrementColumnChange) Matches(x any) bool { } // String implements [Change]. -func (i *incrementColumnChange) String() string { +func (i incrementColumnChange[V]) String() string { return "database.incrementColumnChange" } // Write implements [Change]. -func (i *incrementColumnChange) Write(builder *StatementBuilder) error { +func (i incrementColumnChange[V]) Write(builder *StatementBuilder) error { i.column.WriteUnqualified(builder) builder.WriteString(" = ") - i.column.WriteUnqualified(builder) + builder.WriteArg(i.value) builder.WriteString(" + 1") return nil } -var _ Change = (*incrementColumnChange)(nil) +// WriteArg implements [Change]. +func (i incrementColumnChange[V]) WriteArg(builder *StatementBuilder) { + err := i.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) +} + +var _ Change = (*incrementColumnChange[any])(nil) func NewChangeToStatement(col Column, stmt func(builder *StatementBuilder)) Change { return &changeToStatement{ @@ -293,6 +322,12 @@ func (c *changeToStatement) Write(builder *StatementBuilder) error { return nil } +// WriteArg implements [Change]. +func (c *changeToStatement) WriteArg(builder *StatementBuilder) { + err := c.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) +} + var _ Change = (*changeToStatement)(nil) // CTEChange represents a change that uses a Common Table Expression (CTE). @@ -322,7 +357,7 @@ type cteChange struct { } // IsOnColumn implements [CTEChange]. -func (c *cteChange) IsOnColumn(col Column) bool { +func (c cteChange) IsOnColumn(col Column) bool { return false } @@ -335,11 +370,15 @@ func (c *cteChange) Matches(x any) bool { var expectedCTEBuilder, actualCTEBuilder StatementBuilder c.cte(&expectedCTEBuilder) if c.change != nil { - c.change(c.name).Write(&expectedCTEBuilder) + if err := c.change(c.name).Write(&expectedCTEBuilder); err != nil { + return false + } } toMatch.cte(&actualCTEBuilder) if toMatch.change != nil { - toMatch.change(toMatch.name).Write(&actualCTEBuilder) + if err := toMatch.change(toMatch.name).Write(&actualCTEBuilder); err != nil { + return false + } } if expectedCTEBuilder.String() != actualCTEBuilder.String() { @@ -359,21 +398,26 @@ func (c *cteChange) String() string { } // Write implements [CTEChange]. -func (c *cteChange) Write(builder *StatementBuilder) error { +func (c cteChange) Write(builder *StatementBuilder) error { if c.change == nil { return nil } - c.change(c.name).Write(builder) - return nil + return c.change(c.name).Write(builder) } // WriteCTE implements [CTEChange]. -func (c *cteChange) WriteCTE(builder *StatementBuilder) { +func (c cteChange) WriteCTE(builder *StatementBuilder) { c.cte(builder) } // NoChange implements [NoChange]. // It returns true if there is no change function defined. -func (c *cteChange) NoChange() bool { +func (c cteChange) NoChange() bool { return c.change == nil } + +// WriteArg implements [Change]. +func (c cteChange) WriteArg(builder *StatementBuilder) { + err := c.Write(builder) + logging.New(logging.StreamRuntime).Debug("write arg failed", "error", err) +} diff --git a/backend/v3/storage/database/change_test.go b/backend/v3/storage/database/change_test.go index beaf2aef9..827ec70c8 100644 --- a/backend/v3/storage/database/change_test.go +++ b/backend/v3/storage/database/change_test.go @@ -42,23 +42,32 @@ func TestChangeWrite(t *testing.T) { args: []any{42}, }, }, + { + name: "change to NULL", + change: NewChangeToNull(NewColumn("table", "column")), + want: want{ + stmt: "column = NULL", + args: nil, + }, + }, { name: "multiple changes", change: NewChanges( NewChange(NewColumn("table", "column1"), "value1"), NewChangePtr[int](NewColumn("table", "column2"), nil), NewChange(NewColumn("table", "column3"), 123), + NewChangeToNull(NewColumn("table", "column4")), ), want: want{ - stmt: "column1 = $1, column2 = NULL, column3 = $2", + stmt: "column1 = $1, column2 = NULL, column3 = $2, column4 = NULL", args: []any{"value1", 123}, }, }, { name: "increment change", - change: NewIncrementColumnChange(NewColumn("table", "counter")), + change: NewIncrementColumnChange(NewColumn("table", "counter"), NewColumn("table", "counter")), want: want{ - stmt: "counter = counter + 1", + stmt: "counter = table.counter + 1", }, }, } { @@ -111,11 +120,11 @@ func TestChangeToStatement(t *testing.T) { name: "change to statement with existing builder args", prefillBuilder: func(builder *StatementBuilder) { builder.WriteString("UPDATE table SET ") - NewChanges( + assert.NoError(t, NewChanges( NewChange(NewColumn("table", "field1"), "asdf"), NewChangeToNull(NewColumn("table", "field2")), NewChangeToColumn(NewColumn("table", "field3"), NewColumn("table", "field4")), - ).Write(builder) + ).Write(builder)) builder.WriteString(", ") }, change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) { @@ -135,7 +144,7 @@ func TestChangeToStatement(t *testing.T) { if test.prefillBuilder != nil { test.prefillBuilder(&builder) } - test.change.Write(&builder) + assert.NoError(t, test.change.Write(&builder)) assert.Equal(t, test.want.stmt, builder.String()) assert.Equal(t, builder.Args(), test.want.args) }) @@ -250,7 +259,7 @@ func TestCTEChange(t *testing.T) { if test.afterCTE != nil { test.afterCTE(&builder) } - test.change.Write(&builder) + assert.NoError(t, test.change.Write(&builder)) assert.Equal(t, test.want.stmt, builder.String()) assert.Equal(t, builder.Args(), test.want.args) diff --git a/backend/v3/storage/database/column.go b/backend/v3/storage/database/column.go index a2a510680..0ae79e52b 100644 --- a/backend/v3/storage/database/column.go +++ b/backend/v3/storage/database/column.go @@ -2,6 +2,18 @@ package database import "go.uber.org/mock/gomock" +// Column represents a column in a database table. +type Column interface { + gomock.Matcher + argWriter + // WriteQualified writes the column with the table name as prefix. + WriteQualified(builder *StatementBuilder) + // WriteUnqualified writes the column without the table name as prefix. + WriteUnqualified(builder *StatementBuilder) + // Equals checks if two columns are equal. + Equals(col Column) bool +} + type Columns []Column // Matches implements [Column]. @@ -59,24 +71,22 @@ func (c Columns) Equals(col Column) bool { return true } -var _ Column = (Columns)(nil) - -// Column represents a column in a database table. -type Column interface { - gomock.Matcher - // WriteQualified writes the column with the table name as prefix. - WriteQualified(builder *StatementBuilder) - // WriteUnqualified writes the column without the table name as prefix. - WriteUnqualified(builder *StatementBuilder) - // Equals checks if two columns are equal. - Equals(col Column) bool +// WriteArg implements [Column]. +func (c Columns) WriteArg(builder *StatementBuilder) { + c.WriteQualified(builder) } +var _ Column = (Columns)(nil) + type column struct { table string name string } +func NewColumn(table, name string) Column { + return &column{table: table, name: name} +} + // Matches implements [Column]. func (c column) Matches(x any) bool { toMatch, ok := x.(*column) @@ -91,10 +101,6 @@ func (c column) String() string { return "database.column" } -func NewColumn(table, name string) Column { - return &column{table: table, name: name} -} - // WriteQualified implements [Column]. func (c column) WriteQualified(builder *StatementBuilder) { builder.Grow(len(c.table) + len(c.name) + 1) @@ -120,6 +126,26 @@ func (c *column) Equals(col Column) bool { return c.table == toMatch.table && c.name == toMatch.name } +// WriteArg implements [Column]. +func (c column) WriteArg(builder *StatementBuilder) { + c.WriteQualified(builder) +} + +var _ Column = (*column)(nil) + +type functionColumn struct { + fn function + col Column +} + +type function string + +const ( + _ function = "" + functionLower function = "LOWER" + functionSHA256 function = "SHA256" +) + // LowerColumn returns a column that represents LOWER(col). func LowerColumn(col Column) Column { return &functionColumn{fn: functionLower, col: col} @@ -130,11 +156,6 @@ func SHA256Column(col Column) Column { return &functionColumn{fn: functionSHA256, col: col} } -type functionColumn struct { - fn function - col Column -} - // Matches implements [Column]. func (c *functionColumn) Matches(x any) bool { toMatch, ok := x.(*functionColumn) @@ -155,14 +176,6 @@ func (c *functionColumn) String() string { return "database.functionColumn" } -type function string - -const ( - _ function = "" - functionLower function = "LOWER" - functionSHA256 function = "SHA256" -) - // WriteQualified implements [Column]. func (c functionColumn) WriteQualified(builder *StatementBuilder) { builder.Grow(len(c.fn) + 2) @@ -193,4 +206,9 @@ func (c *functionColumn) Equals(col Column) bool { return c.col.Equals(toMatch.col) } +// WriteArg implements [Column]. +func (c functionColumn) WriteArg(builder *StatementBuilder) { + c.WriteQualified(builder) +} + var _ Column = (*functionColumn)(nil) diff --git a/backend/v3/storage/database/dialect/postgres/migration/010_authorizations_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/010_authorizations_table/up.sql index 7b4380f68..3d7290e73 100644 --- a/backend/v3/storage/database/dialect/postgres/migration/010_authorizations_table/up.sql +++ b/backend/v3/storage/database/dialect/postgres/migration/010_authorizations_table/up.sql @@ -14,7 +14,6 @@ CREATE TABLE zitadel.authorizations created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (instance_id, id), --- FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE, FOREIGN KEY (instance_id, project_id) REFERENCES zitadel.projects (instance_id, id) ON DELETE CASCADE, FOREIGN KEY (instance_id, grant_id) REFERENCES zitadel.project_grants (instance_id, id) ON DELETE CASCADE ); diff --git a/backend/v3/storage/database/dialect/postgres/migration/011_session_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/011_session_table/up.sql index 8104923c6..50b3de1ef 100644 --- a/backend/v3/storage/database/dialect/postgres/migration/011_session_table/up.sql +++ b/backend/v3/storage/database/dialect/postgres/migration/011_session_table/up.sql @@ -22,7 +22,6 @@ CREATE TABLE zitadel.sessions ( , PRIMARY KEY (instance_id, id) , FOREIGN KEY (instance_id) REFERENCES zitadel.instances(id) --- , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE , FOREIGN KEY (instance_id, user_agent_id) REFERENCES zitadel.session_user_agents(instance_id, fingerprint_id) ON DELETE SET NULL (user_agent_id) ); diff --git a/backend/v3/storage/database/dialect/postgres/migration/012_identity_provider_intents_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/012_identity_provider_intents_table/up.sql index cfc23579d..8ee03d664 100644 --- a/backend/v3/storage/database/dialect/postgres/migration/012_identity_provider_intents_table/up.sql +++ b/backend/v3/storage/database/dialect/postgres/migration/012_identity_provider_intents_table/up.sql @@ -31,8 +31,6 @@ CREATE TABLE zitadel.identity_provider_intents expires_at TIMESTAMPTZ, PRIMARY KEY (instance_id, id), - -- TODO(@adlerhurst): easiest would be to create the index in the user migrations so that we can merge this one but the problem is that the migration might fail if there are already intents created. - -- FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE, FOREIGN KEY (instance_id, idp_id) REFERENCES zitadel.identity_providers (instance_id, id) ON DELETE CASCADE ); diff --git a/backend/v3/storage/database/dialect/postgres/migration/013_user_table.go b/backend/v3/storage/database/dialect/postgres/migration/013_user_table.go new file mode 100644 index 000000000..991b35179 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/013_user_table.go @@ -0,0 +1,16 @@ +package migration + +import ( + _ "embed" +) + +var ( + //go:embed 013_user_table/up.sql + up013UserTable string + //go:embed 013_user_table/down.sql + down013UserTable string +) + +func init() { + registerSQLMigration(13, up013UserTable, down013UserTable) +} diff --git a/backend/v3/storage/database/dialect/postgres/migration/013_user_table/down.sql b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/down.sql new file mode 100644 index 000000000..fa7854028 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/down.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS zitadel.user_identity_provider_links; +DROP TABLE IF EXISTS zitadel.user_passkeys; +DROP TYPE IF EXISTS zitadel.passkey_type; +DROP TABLE IF EXISTS zitadel.machine_keys; +DROP TABLE IF EXISTS zitadel.user_personal_access_tokens; +DROP TABLE IF EXISTS zitadel.user_metadata CASCADE; + +ALTER TABLE zitadel.identity_provider_intents DROP CONSTRAINT IF EXISTS fk_idp_intent_user; +ALTER TABLE zitadel.sessions DROP CONSTRAINT IF EXISTS fk_session_user; +ALTER TABLE zitadel.authorizations DROP CONSTRAINT IF EXISTS fk_authorization_user; + +DROP TABLE IF EXISTS zitadel.users; +DROP TYPE IF EXISTS zitadel.user_type; +DROP TYPE IF EXISTS zitadel.user_state; diff --git a/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql new file mode 100644 index 000000000..dcc229065 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql @@ -0,0 +1,228 @@ +CREATE TYPE zitadel.user_state AS ENUM ( + 'initial' + , 'active' + , 'inactive' + , 'locked' + , 'suspended' +); + +CREATE TYPE zitadel.user_type AS ENUM ( + 'human' + , 'machine' +); + +CREATE TABLE zitadel.users( + instance_id TEXT NOT NULL + , organization_id TEXT NOT NULL + , id TEXT NOT NULL CHECK (id <> '') + + , username TEXT NOT NULL CHECK (username <> '') + , username_org_unique BOOLEAN DEFAULT FALSE NOT NULL -- this field MUST be filled if the username must be unique on organization level + , state zitadel.user_state NOT NULL DEFAULT 'active' + , type zitadel.user_type NOT NULL + + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + + , PRIMARY KEY (instance_id, id) + , FOREIGN KEY (instance_id, organization_id) REFERENCES zitadel.organizations(instance_id, id) ON DELETE CASCADE + + -- human + + , first_name TEXT CHECK ((type = 'machine' AND first_name IS NULL) OR (type = 'human' AND first_name <> '')) + , last_name TEXT CHECK ((type = 'machine' AND last_name IS NULL) OR (type = 'human' AND last_name <> '')) + , nickname TEXT CHECK ((type = 'machine' AND nickname IS NULL) OR (type = 'human')) + , display_name TEXT CHECK ((type = 'machine' AND display_name IS NULL) OR (type = 'human')) + , preferred_language TEXT CHECK ((type = 'machine' AND preferred_language IS NULL) OR (type = 'human')) + , gender SMALLINT CHECK ((type = 'machine' AND gender IS NULL) OR (type = 'human')) + , avatar_key TEXT CHECK ((type = 'machine' AND avatar_key IS NULL) OR (type = 'human')) + , multifactor_initialization_skipped_at TIMESTAMPTZ CHECK ((type = 'machine' AND multifactor_initialization_skipped_at IS NULL) OR (type = 'human')) + + , password_hash TEXT CHECK ((type = 'machine' AND password_hash IS NULL) OR (type = 'human')) + , password_change_required BOOLEAN CHECK ((type = 'machine' AND password_change_required IS NULL) OR (type = 'human')) + , password_changed_at TIMESTAMPTZ CHECK ((type = 'machine' AND password_changed_at IS NULL) OR (type = 'human')) + , password_verification_id TEXT CHECK ((type = 'machine' AND password_verification_id IS NULL) OR (type = 'human')) -- used for reset password flow + , password_last_successful_check TIMESTAMPTZ CHECK ((type = 'machine' AND password_last_successful_check IS NULL) OR (type = 'human')) + , password_failed_attempts SMALLINT CHECK ((type = 'machine' AND password_failed_attempts IS NULL) OR (type = 'human') AND (password_failed_attempts >= 0)) + + , email TEXT CHECK ((type = 'machine' AND email IS NULL) OR (type = 'human')) + , unverified_email TEXT CHECK ((type = 'machine' AND unverified_email IS NULL) OR (type = 'human' AND unverified_email <> '')) -- after successful verification this column is not cleared. + , email_verified_at TIMESTAMPTZ CHECK ((type = 'machine' AND email_verified_at IS NULL) OR (type = 'human')) + , email_verification_id TEXT CHECK ((type = 'machine' AND email_verification_id IS NULL) OR (type = 'human' AND (email IS DISTINCT FROM unverified_email OR email_verification_id IS NULL))) + , email_otp_enabled_at TIMESTAMPTZ CHECK ((type = 'machine' AND email_otp_enabled_at IS NULL) OR (type = 'human')) + , email_otp_last_successful_check TIMESTAMPTZ CHECK ((type = 'machine' AND email_otp_last_successful_check IS NULL) OR (type = 'human')) + , email_otp_failed_attempts SMALLINT CHECK ((type = 'machine' AND email_otp_failed_attempts IS NULL) OR (type = 'human')) + + , phone TEXT CHECK ((type = 'machine' AND phone IS NULL) OR (type = 'human')) + , unverified_phone TEXT CHECK ((type = 'machine' AND unverified_phone IS NULL) OR (type = 'human')) -- after successful verification this column is not cleared. + , phone_verified_at TIMESTAMPTZ CHECK ((type = 'machine' AND phone_verified_at IS NULL) OR (type = 'human')) + , phone_verification_id TEXT CHECK ((type = 'machine' AND phone_verification_id IS NULL) OR (type = 'human' AND (phone IS DISTINCT FROM unverified_phone OR phone_verification_id IS NULL))) + , sms_otp_enabled_at TIMESTAMPTZ CHECK ((type = 'machine' AND sms_otp_enabled_at IS NULL) OR (type = 'human')) + , sms_otp_last_successful_check TIMESTAMPTZ CHECK ((type = 'machine' AND sms_otp_last_successful_check IS NULL) OR (type = 'human')) + , sms_otp_failed_attempts SMALLINT CHECK ((type = 'machine' AND sms_otp_failed_attempts IS NULL) OR (type = 'human')) + + , totp_secret BYTEA CHECK ((type = 'machine' AND totp_secret IS NULL) OR (type = 'human')) + , totp_verified_at TIMESTAMPTZ CHECK ((type = 'machine' AND totp_verified_at IS NULL) OR (type = 'human')) + , totp_last_successful_check TIMESTAMPTZ CHECK ((type = 'machine' AND totp_last_successful_check IS NULL) OR (type = 'human')) + , totp_failed_attempts SMALLINT CHECK ((type = 'machine' AND totp_failed_attempts IS NULL) OR (type = 'human')) + + , invite_verification_id TEXT CHECK ((type = 'machine' AND invite_verification_id IS NULL) OR (type = 'human')) + , invite_accepted_at TIMESTAMPTZ CHECK ((type = 'machine' AND invite_accepted_at IS NULL) OR (type = 'human')) + , invite_failed_attempts SMALLINT CHECK ((type = 'machine' AND invite_failed_attempts IS NULL) OR (type = 'human')) + + -- foreign keys for verifications are created in the verification migration + + -- machine + + , name TEXT CHECK ((type = 'human' AND name IS NULL) OR (type = 'machine')) + , description TEXT CHECK ((type = 'human' AND description IS NULL) OR (type = 'machine')) + , secret TEXT CHECK ((type = 'human' AND secret IS NULL) OR (type = 'machine')) + , access_token_type SMALLINT CHECK ((type = 'human' AND access_token_type IS NULL) OR (type = 'machine')) +); + +-- previously created tables +ALTER TABLE zitadel.identity_provider_intents ADD CONSTRAINT fk_idp_intent_user FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE; +ALTER TABLE zitadel.sessions ADD CONSTRAINT fk_session_user FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE; +ALTER TABLE zitadel.authorizations ADD CONSTRAINT fk_authorization_user FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE; + +-- user +CREATE UNIQUE INDEX ON zitadel.users(instance_id, organization_id, username) WHERE username_org_unique IS TRUE; --TODO(adlerhurst): does that work if a username is already present on a user without org unique? +CREATE UNIQUE INDEX ON zitadel.users(instance_id, username) WHERE username_org_unique IS FALSE; +CREATE INDEX idx_user_username ON zitadel.users (username); +CREATE INDEX idx_user_username_lower ON zitadel.users (lower(username)); +CREATE INDEX idx_machine_name ON zitadel.users (name); +CREATE INDEX idx_human_email ON zitadel.users (email); +CREATE INDEX idx_human_email_lower ON zitadel.users (lower(email)); +CREATE INDEX idx_human_phone ON zitadel.users (phone); +CREATE INDEX idx_human_phone_lower ON zitadel.users (lower(phone)); + +-- human +CREATE UNIQUE INDEX ON zitadel.users(password_verification_id) WHERE password_verification_id IS NOT NULL; +CREATE UNIQUE INDEX ON zitadel.users(email_verification_id) WHERE email_verification_id IS NOT NULL; +CREATE UNIQUE INDEX ON zitadel.users(phone_verification_id) WHERE phone_verification_id IS NOT NULL; + +CREATE TRIGGER trigger_set_updated_at +BEFORE UPDATE ON zitadel.users +FOR EACH ROW +WHEN (NEW.updated_at IS NULL) +EXECUTE FUNCTION zitadel.set_updated_at(); + +-- ---------------------------------------------------------------- +-- user metadata +-- ---------------------------------------------------------------- + +CREATE TABLE zitadel.user_metadata ( + instance_id TEXT NOT NULL + , user_id TEXT NOT NULL + , key TEXT NOT NULL CHECK (key <> '') + , value BYTEA NOT NULL + + , created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + + , PRIMARY KEY (instance_id, user_id, key) + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE +); + +CREATE INDEX idx_user_metadata_key ON zitadel.user_metadata (key); +CREATE INDEX idx_user_metadata_value ON zitadel.user_metadata (sha256(value)); + +CREATE TRIGGER trg_set_updated_at_user_metadata + BEFORE INSERT OR UPDATE ON zitadel.user_metadata + FOR EACH ROW + WHEN (NEW.updated_at IS NULL) + EXECUTE FUNCTION zitadel.set_updated_at(); + +-- ---------------------------------------------------------------- +-- personal access tokens +-- ---------------------------------------------------------------- + +CREATE TABLE zitadel.user_personal_access_tokens( + instance_id TEXT NOT NULL + , user_id TEXT NOT NULL + + , id TEXT NOT NULL + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , expires_at TIMESTAMPTZ + , scopes TEXT[] + + , PRIMARY KEY (instance_id, id) + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE +); + +-- ---------------------------------------------------------------- +-- machine keys +-- ---------------------------------------------------------------- + +CREATE TABLE zitadel.machine_keys( + instance_id TEXT NOT NULL + , user_id TEXT NOT NULL + + , id TEXT NOT NULL + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , expires_at TIMESTAMPTZ + + , type SMALLINT NOT NULL CHECK (type >= 0) + , public_key BYTEA NOT NULL + + , PRIMARY KEY (instance_id, id) + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE +); + +-- ---------------------------------------------------------------- +-- passkeys +-- ---------------------------------------------------------------- + +CREATE TYPE zitadel.passkey_type AS ENUM ( + 'passwordless' + , 'u2f' +); + +CREATE TABLE zitadel.user_passkeys( + instance_id TEXT NOT NULL + , token_id TEXT NOT NULL + , key_id BYTEA NOT NULL + + , user_id TEXT NOT NULL + + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + , verified_at TIMESTAMPTZ + + , type zitadel.passkey_type NOT NULL + , name TEXT NOT NULL CHECK (name <> '') + , sign_count INT NOT NULL DEFAULT 0 CHECK (sign_count >= 0) + , challenge BYTEA NOT NULL + , public_key BYTEA NOT NULL + , attestation_type TEXT NOT NULL CHECK (attestation_type <> '') + , authenticator_attestation_guid BYTEA NOT NULL + , relying_party_id TEXT NOT NULL CHECK (relying_party_id <> '') + + , PRIMARY KEY (instance_id, token_id) + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE +); + +CREATE INDEX idx_user_passkeys_challenge ON zitadel.user_passkeys (sha256(challenge)); +CREATE INDEX idx_user_passkeys_key_id ON zitadel.user_passkeys (key_id); +CREATE INDEX idx_user_passkeys_type ON zitadel.user_passkeys (type); + +-- ---------------------------------------------------------------- +-- identity provider links +-- ---------------------------------------------------------------- + +CREATE TABLE zitadel.user_identity_provider_links( + instance_id TEXT NOT NULL + , identity_provider_id TEXT NOT NULL + , user_id TEXT NOT NULL + + , provided_user_id TEXT NOT NULL CHECK(provided_user_id <> '') + , provided_username TEXT NOT NULL + + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + + , PRIMARY KEY (instance_id, identity_provider_id, provided_user_id) + + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE + , FOREIGN KEY (instance_id, identity_provider_id) REFERENCES zitadel.identity_providers(instance_id, id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/backend/v3/storage/database/dialect/postgres/migration/014_verification_table.go b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table.go new file mode 100644 index 000000000..4fe5407f9 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table.go @@ -0,0 +1,16 @@ +package migration + +import ( + _ "embed" +) + +var ( + //go:embed 014_verification_table/up.sql + up014VerificationTable string + //go:embed 014_verification_table/down.sql + down014VerificationTable string +) + +func init() { + registerSQLMigration(14, up014VerificationTable, down014VerificationTable) +} diff --git a/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/down.sql b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/down.sql new file mode 100644 index 000000000..186e55eab --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/down.sql @@ -0,0 +1,7 @@ +ALTER TABLE zitadel.users DROP CONSTRAINT IF EXISTS fk_unverified_password; +ALTER TABLE zitadel.users DROP CONSTRAINT IF EXISTS fk_unverified_email; +ALTER TABLE zitadel.users DROP CONSTRAINT IF EXISTS fk_unverified_phone; + +DROP TRIGGER IF EXISTS user_verification_integrity_trigger ON zitadel.users; +DROP FUNCTION IF EXISTS zitadel.ensure_user_verification_integrity() CASCADE; +DROP TABLE IF EXISTS zitadel.verifications CASCADE; diff --git a/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/up.sql new file mode 100644 index 000000000..3d2922295 --- /dev/null +++ b/backend/v3/storage/database/dialect/postgres/migration/014_verification_table/up.sql @@ -0,0 +1,61 @@ +CREATE TABLE zitadel.verifications( + instance_id TEXT NOT NULL + , user_id TEXT + , id TEXT NOT NULL DEFAULT gen_random_uuid()::TEXT + + , code BYTEA + + , created_at TIMESTAMPTZ NOT NULL DEFAULT now() + , updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + , expiry INTERVAL + + , failed_attempts SMALLINT NOT NULL DEFAULT 0 CHECK (failed_attempts >= 0) + + , PRIMARY KEY (instance_id, id) + , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE +); + +ALTER TABLE zitadel.users ADD CONSTRAINT fk_unverified_password FOREIGN KEY (instance_id, password_verification_id) REFERENCES zitadel.verifications(instance_id, id) ON DELETE SET NULL (password_verification_id); +ALTER TABLE zitadel.users ADD CONSTRAINT fk_unverified_email FOREIGN KEY (instance_id, email_verification_id) REFERENCES zitadel.verifications(instance_id, id) ON DELETE SET NULL (email_verification_id); +ALTER TABLE zitadel.users ADD CONSTRAINT fk_unverified_phone FOREIGN KEY (instance_id, phone_verification_id) REFERENCES zitadel.verifications(instance_id, id) ON DELETE SET NULL (phone_verification_id); + +CREATE OR REPLACE FUNCTION zitadel.ensure_user_verification_integrity() RETURNS trigger AS $$ +BEGIN + IF (NEW.password_verification_id IS DISTINCT FROM OLD.password_verification_id) THEN + DELETE FROM zitadel.verifications + WHERE instance_id = NEW.instance_id + AND id = OLD.password_verification_id; + END IF; + + IF (NEW.email_verification_id IS DISTINCT FROM OLD.email_verification_id) THEN + DELETE FROM zitadel.verifications + WHERE instance_id = NEW.instance_id + AND id = OLD.email_verification_id; + END IF; + + IF (NEW.phone_verification_id IS DISTINCT FROM OLD.phone_verification_id) THEN + DELETE FROM zitadel.verifications + WHERE instance_id = NEW.instance_id + AND id = OLD.phone_verification_id; + END IF; + + IF (NEW.invite_verification_id IS DISTINCT FROM OLD.invite_verification_id) THEN + DELETE FROM zitadel.verifications + WHERE instance_id = NEW.instance_id + AND id = OLD.invite_verification_id; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER user_verification_integrity_trigger +AFTER UPDATE ON zitadel.users +FOR EACH ROW +EXECUTE FUNCTION zitadel.ensure_user_verification_integrity(); + +CREATE TRIGGER trigger_set_updated_at +BEFORE UPDATE ON zitadel.verifications +FOR EACH ROW +WHEN (NEW.updated_at IS NULL) +EXECUTE FUNCTION zitadel.set_updated_at(); \ No newline at end of file diff --git a/backend/v3/storage/database/events_testing/authorization_test.go b/backend/v3/storage/database/events_testing/authorization_test.go index bf0b8fd9d..abf9f90fb 100644 --- a/backend/v3/storage/database/events_testing/authorization_test.go +++ b/backend/v3/storage/database/events_testing/authorization_test.go @@ -435,6 +435,8 @@ func TestServer_AuthorizationReduces(t *testing.T) { } func createAndEnsureAuthorization(t *testing.T, instanceID, orgID, userID, projectID string, roles []string, retryDuration time.Duration, tick time.Duration) *authorization_v2.CreateAuthorizationResponse { + t.Helper() + // create authorization createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{ UserId: userID, @@ -454,11 +456,13 @@ func createAndEnsureAuthorization(t *testing.T, instanceID, orgID, userID, proje assert.Equal(collect, domain.AuthorizationStateActive, az.State) assert.Equal(collect, createdAuthorization.GetCreationDate().AsTime().UTC(), az.CreatedAt.UTC()) assert.Equal(collect, createdAuthorization.GetCreationDate().AsTime().UTC(), az.UpdatedAt.UTC()) - }, retryDuration, tick, "authorization not found within %v: %v", retryDuration, err) + }, retryDuration, tick, "authorization %q not found within %v: %v", createdAuthorization.Id, retryDuration, err) return createdAuthorization } func prepareProjectAndProjectRoles(t *testing.T, orgID string, roles []string) string { + t.Helper() + project, err := ProjectClient.CreateProject(CTX, &project_v2beta.CreateProjectRequest{ OrganizationId: orgID, Name: integration.ProjectName(), @@ -477,7 +481,7 @@ func prepareProjectAndProjectRoles(t *testing.T, orgID string, roles []string) s projectRepo.PrimaryKeyCondition(Instance.ID(), project.Id), )) require.NoError(collect, err) - }, retryDuration, tick, "project not found") + }, retryDuration, tick, "project %q not found within %v: %v", project.Id, retryDuration, err) for _, role := range roles { _, err = ProjectClient.AddProjectRole(CTX, &project_v2beta.AddProjectRoleRequest{ diff --git a/backend/v3/storage/database/events_testing/idp_intent_test.go b/backend/v3/storage/database/events_testing/idp_intent_test.go index c9d99df16..ca64360f2 100644 --- a/backend/v3/storage/database/events_testing/idp_intent_test.go +++ b/backend/v3/storage/database/events_testing/idp_intent_test.go @@ -2,7 +2,7 @@ package events_test -// TODO(adlerhurst): Uncomment once users table is created and FK can be enforced +// The tests of this file fail because we would need to mock an ldap, OIDC and Oauth server. // import ( // "net" diff --git a/backend/v3/storage/database/events_testing/session_test.go b/backend/v3/storage/database/events_testing/session_test.go index 064ff52f8..f240f5e22 100644 --- a/backend/v3/storage/database/events_testing/session_test.go +++ b/backend/v3/storage/database/events_testing/session_test.go @@ -325,7 +325,7 @@ func TestServer_SessionReduces(t *testing.T) { Metadata: metadata, UserAgent: userAgent, }, dbSession) - }, retryDuration, tick, "session not found within %v: %v", retryDuration, err) + }, retryDuration, tick, "session %q not found within %v: %v", createdSession.GetSessionId(), retryDuration, err) }) t.Run("checks with challenges set reduces", func(t *testing.T) { diff --git a/backend/v3/storage/database/events_testing/users_test.go b/backend/v3/storage/database/events_testing/users_test.go new file mode 100644 index 000000000..5081d42fa --- /dev/null +++ b/backend/v3/storage/database/events_testing/users_test.go @@ -0,0 +1,1107 @@ +//go:build integration + +package events_test + +import ( + "bytes" + _ "embed" + "encoding/json" + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/go-resty/resty/v2" + "github.com/muhlemmer/gu" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/headzoo/surf.v1" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/pkg/grpc/management" + object "github.com/zitadel/zitadel/pkg/grpc/object/v2" + "github.com/zitadel/zitadel/pkg/grpc/user" + v2_user "github.com/zitadel/zitadel/pkg/grpc/user/v2" +) + +func TestServer_TestHumanUserReduces(t *testing.T) { + instanceID := Instance.ID() + orgID := Instance.DefaultOrg.Id + + userRepo := repository.UserRepository() + + t.Run("test human user added reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + before := time.Now() + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.added + // event user.human.added + // domain.User + assert.Equal(t, instanceID, user.InstanceID) + assert.Equal(t, orgID, user.OrganizationID) + assert.Equal(t, resp.UserId, user.ID) + assert.Equal(t, humanUserRequest.UserName, user.Username) + assert.Equal(t, domain.UserStateActive, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + assert.WithinRange(t, user.CreatedAt, before, after) + // Email + assert.Equal(t, humanUserRequest.Email.Email, user.Human.Email.Address) + assert.NotZero(t, user.Human.Email.VerifiedAt) + assert.Nil(t, user.Human.Email.PendingVerification) + // Phone + if !assert.NotNil(t, humanUserRequest.Phone) { + assert.Equal(t, humanUserRequest.Phone.Phone, user.Human.Phone.Number) + assert.NotZero(t, user.Human.Phone.VerifiedAt) + assert.Nil(t, user.Human.Phone.PendingVerification) + } + // Human + assert.Equal(t, humanUserRequest.Profile.FirstName, user.Human.FirstName) + assert.Equal(t, humanUserRequest.Profile.LastName, user.Human.LastName) + assert.Equal(t, humanUserRequest.Profile.NickName, user.Human.Nickname) + assert.Equal(t, humanUserRequest.Profile.DisplayName, user.Human.DisplayName) + assert.Equal(t, humanUserRequest.Profile.PreferredLanguage, user.Human.PreferredLanguage.String()) + assert.EqualValues(t, humanUserRequest.Profile.Gender, user.Human.Gender) + }, retryDuration, tick) + }) + + t.Run("test human user register reduced", func(t *testing.T) { + token := integration.SystemToken + client := resty.New() + + bow := surf.NewBrowser() + err := bow.Open("http://localhost:8082" + "/ui/login/register/org") + require.NoError(t, err) + require.Equal(t, 200, bow.StatusCode()) + + csfr, err := bow.Forms()[1].Value("gorilla.csrf.Token") + require.NoError(t, err) + + before := time.Now() + client.SetCookieJar(bow.CookieJar()) + firstName := gofakeit.Name() + lastName := gofakeit.Name() + email := gofakeit.Email() + out, err := client.R().SetAuthToken(token). + SetFormData(map[string]string{ + "gorilla.csrf.Token": csfr, + "orgname": gofakeit.Name(), + "firstname": firstName, + "lastname": lastName, + "email": email, + "register-password": "Password1!", + "register-password-confirmation": "Password1!", + }). + Post("http://localhost:8082" + "/ui/login/register/org") + + require.NoError(t, err) + require.Equal(t, 200, out.StatusCode()) + after := time.Now() + + ctx := t.Context() + instanceRepo := repository.InstanceRepository() + instance, err := instanceRepo.Get(ctx, pool, database.WithCondition(instanceRepo.NameCondition(database.TextOperationEqual, "ZITADEL"))) + assert.NoError(t, err) + instanceID := instance.ID + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.Human().FirstNameCondition(database.TextOperationEqual, firstName), + userRepo.Human().LastNameCondition(database.TextOperationEqual, lastName), + )), + ) + require.NoError(t, err) + + // domain.User + assert.Equal(t, instanceID, user.InstanceID) + assert.Equal(t, email, user.Username) + assert.Equal(t, domain.UserStateActive, user.State) + assert.WithinRange(t, user.CreatedAt, before, after) + // Email + assert.Equal(t, email, user.Human.Email.UnverifiedAddress) + assert.Zero(t, user.Human.Email.VerifiedAt) + assert.Nil(t, user.Human.Email.PendingVerification) + // Human + assert.Equal(t, firstName, user.Human.FirstName) + assert.Equal(t, lastName, user.Human.LastName) + }, retryDuration, tick) + }) + + t.Run("test human user locked reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateActive, user.State) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.LockUser(IAMCTX, &management.LockUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.locked + assert.Equal(t, domain.UserStateLocked, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test human user unlock reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateActive, user.State) + }, retryDuration, tick) + + _, err = MgmtClient.LockUser(IAMCTX, &management.LockUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateLocked, user.State) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.UnlockUser(IAMCTX, &management.UnlockUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.unlocked + assert.Equal(t, domain.UserStateActive, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test human user deactivate reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateActive, user.State) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.DeactivateUser(IAMCTX, &management.DeactivateUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.deactivated + assert.Equal(t, domain.UserStateInactive, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test human user reactivate reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateActive, user.State) + }, retryDuration, tick) + + _, err = MgmtClient.DeactivateUser(IAMCTX, &management.DeactivateUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.Equal(t, domain.UserStateInactive, user.State) + }, retryDuration, tick) + + before := time.Now() + _, err = MgmtClient.ReactivateUser(IAMCTX, &management.ReactivateUserRequest{ + Id: resp.UserId, + }) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.reactivated + assert.Equal(t, domain.UserStateActive, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test human user removed reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.NotNil(t, user) + }, retryDuration, tick) + + _, err = UserClient.DeleteUser(IAMCTX, &v2_user.DeleteUserRequest{ + UserId: resp.UserId, + }) + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + _, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + + // event user.removed + require.ErrorIs(t, &database.NoRowFoundError{}, err) + }, retryDuration, tick) + }) + + t.Run("test human user change name reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.NotNil(t, user) + }, retryDuration, tick) + + username := gofakeit.Username() + _, err = UserClient.UpdateHumanUser(IAMCTX, &v2_user.UpdateHumanUserRequest{ + UserId: resp.UserId, + Username: &username, + }) + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.username.changed + assert.Equal(t, username, user.Username) + }, retryDuration, tick) + }) + + t.Run("test human user profile change reduced", func(t *testing.T) { + humanUserRequest := &management.AddHumanUserRequest{ + UserName: gofakeit.Username(), + Profile: &management.AddHumanUserRequest_Profile{ + FirstName: "first", + LastName: "last", + NickName: "nick", + DisplayName: "display", + PreferredLanguage: "en", + Gender: user.Gender_GENDER_MALE, + }, + Email: &management.AddHumanUserRequest_Email{ + Email: gofakeit.Email(), + IsEmailVerified: true, + }, + Phone: &management.AddHumanUserRequest_Phone{ + Phone: "+1" + gofakeit.Phone(), + IsPhoneVerified: true, + }, + InitialPassword: "Password1!", + } + + resp, err := MgmtClient.AddHumanUser(IAMCTX, humanUserRequest) + require.NoError(t, err) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + assert.NotNil(t, user) + }, retryDuration, tick) + + profile := &v2_user.SetHumanProfile{ + GivenName: gofakeit.FirstName(), + FamilyName: gofakeit.LastName(), + NickName: gu.Ptr(gofakeit.Color()), + DisplayName: gu.Ptr(gofakeit.Name()), + PreferredLanguage: gu.Ptr("fr"), + Gender: gu.Ptr(v2_user.Gender_GENDER_FEMALE), + } + _, err = UserClient.UpdateHumanUser(IAMCTX, &v2_user.UpdateHumanUserRequest{ + UserId: resp.UserId, + Profile: profile, + }) + require.NoError(t, err) + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.human.profile.changed + assert.Equal(t, profile.GivenName, user.Human.FirstName) + assert.Equal(t, profile.FamilyName, user.Human.LastName) + assert.Equal(t, *profile.NickName, user.Human.Nickname) + assert.Equal(t, *profile.DisplayName, user.Human.DisplayName) + assert.Equal(t, *profile.PreferredLanguage, user.Human.PreferredLanguage.String()) + assert.EqualValues(t, *profile.Gender, user.Human.Gender) + }, retryDuration, tick) + }) + + // I added a delete user command (Over REST because there is no gRPC client (as far as I can tell) for the default instance) + // the issue is if the user does not exist it will throw and error when trying to delete it + t.Run("test human user avatar added reduced", func(t *testing.T) { + ctx := t.Context() + token := integration.SystemToken + + instanceRepo := repository.InstanceRepository() + instance, err := instanceRepo.Get(ctx, pool, database.WithCondition(instanceRepo.NameCondition(database.TextOperationEqual, "ZITADEL"))) + instanceID := instance.ID + require.NoError(t, err) + orgRepo := repository.OrganizationRepository() + org_, err := orgRepo.Get(ctx, pool, database.WithCondition(database.And(orgRepo.InstanceIDCondition(instanceID), orgRepo.NameCondition(database.TextOperationEqual, "ZITADEL")))) + require.NoError(t, err) + orgID := org_.ID + + // delete user tester + deleteUserReq := v2_user.DeleteUserRequest{ + UserId: "tester", + } + deleteUserReqJSON, err := json.Marshal(&deleteUserReq) + require.NoError(t, err) + client := resty.New() + out, err := client.R().SetAuthToken(token). + SetHeader("x-zitadel-orgid", orgID). + SetBody(deleteUserReqJSON). + Delete("http://localhost:8082" + "/v2beta/users/human") + require.NoError(t, err) + // require.Equal(t, 200, out.StatusCode()) + // require.NoError(t, err) + + // create user tester + createUserReq := &v2_user.AddHumanUserRequest{ + UserId: gu.Ptr("tester"), // YOLO + Organization: &object.Organization{ + Org: &object.Organization_OrgId{ + OrgId: orgID, + }, + }, + Profile: &v2_user.SetHumanProfile{ + GivenName: "Donald", + FamilyName: "Duck", + NickName: gu.Ptr("Dukkie"), + DisplayName: gu.Ptr("Donald Duck"), + PreferredLanguage: gu.Ptr("en"), + Gender: v2_user.Gender_GENDER_DIVERSE.Enum(), + }, + Email: &v2_user.SetHumanEmail{ + Email: gofakeit.Email(), + }, + Phone: &v2_user.SetHumanPhone{}, + Metadata: []*v2_user.SetMetadataEntry{ + { + Key: "somekey", + Value: []byte("somevalue"), + }, + }, + PasswordType: &v2_user.AddHumanUserRequest_Password{ + Password: &v2_user.Password{ + Password: "DifficultPW666!", + ChangeRequired: true, + }, + }, + } + createUserReqJSON, err := json.Marshal(createUserReq) + require.NoError(t, err) + client = resty.New() + out, err = client.R().SetAuthToken(token). + SetHeader("x-zitadel-orgid", orgID). + SetBody(createUserReqJSON). + Post("http://localhost:8082" + "/v2beta/users/human") + require.NoError(t, err) + // require.Equal(t, 201, out.StatusCode()) + + require.NoError(t, err) + + // client = resty.New() + // POST avatar + before := time.Now() + out, err = client.R().SetAuthToken(token). + SetMultipartField("file", "filename", "image/png", bytes.NewReader(picture)). + SetHeader("x-zitadel-orgid", orgID). + Post("http://localhost:8082" + "/assets/v1" + "/users/me/avatar") + require.NoError(t, err) + require.Equal(t, 200, out.StatusCode()) + after := time.Now() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition("tester"), + )), + ) + require.NoError(t, err) + + // event user.human.avatar.added + avatarUrl := "users/tester/avatar?" + assert.Contains(t, user.Human.AvatarKey, avatarUrl) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + // I added a delete user command (Over REST because there is no gRPC client (as far as I can tell) for the default instance) + // the issue is if the user does not exist it will throw and error when trying to delete it + t.Run("test human user avatar removed reduced", func(t *testing.T) { + ctx := t.Context() + token := integration.SystemToken + + instanceRepo := repository.InstanceRepository() + instance, err := instanceRepo.Get(ctx, pool, database.WithCondition(instanceRepo.NameCondition(database.TextOperationEqual, "ZITADEL"))) + instanceID := instance.ID + require.NoError(t, err) + orgRepo := repository.OrganizationRepository() + org_, err := orgRepo.Get(ctx, pool, database.WithCondition(database.And(orgRepo.InstanceIDCondition(instanceID), orgRepo.NameCondition(database.TextOperationEqual, "ZITADEL")))) + require.NoError(t, err) + orgID := org_.ID + + // delete user tester + deleteUserReq := v2_user.DeleteUserRequest{ + UserId: "tester", + } + deleteUserReqJSON, err := json.Marshal(&deleteUserReq) + require.NoError(t, err) + client := resty.New() + out, err := client.R().SetAuthToken(token). + SetHeader("x-zitadel-orgid", orgID). + SetBody(deleteUserReqJSON). + Delete("http://localhost:8082" + "/v2beta/users/human") + require.NoError(t, err) + // require.Equal(t, 200, out.StatusCode()) + // require.NoError(t, err) + + // create user tester + createUserReq := &v2_user.AddHumanUserRequest{ + UserId: gu.Ptr("tester"), // YOLO + Organization: &object.Organization{ + Org: &object.Organization_OrgId{ + OrgId: orgID, + }, + }, + Profile: &v2_user.SetHumanProfile{ + GivenName: "Donald", + FamilyName: "Duck", + NickName: gu.Ptr("Dukkie"), + DisplayName: gu.Ptr("Donald Duck"), + PreferredLanguage: gu.Ptr("en"), + Gender: v2_user.Gender_GENDER_DIVERSE.Enum(), + }, + Email: &v2_user.SetHumanEmail{ + Email: gofakeit.Email(), + }, + Phone: &v2_user.SetHumanPhone{}, + Metadata: []*v2_user.SetMetadataEntry{ + { + Key: "somekey", + Value: []byte("somevalue"), + }, + }, + PasswordType: &v2_user.AddHumanUserRequest_Password{ + Password: &v2_user.Password{ + Password: "DifficultPW666!", + ChangeRequired: true, + }, + }, + } + createUserReqJSON, err := json.Marshal(createUserReq) + require.NoError(t, err) + client = resty.New() + out, err = client.R().SetAuthToken(token). + SetHeader("x-zitadel-orgid", orgID). + SetBody(createUserReqJSON). + Post("http://localhost:8082" + "/v2beta/users/human") + require.NoError(t, err) + // require.Equal(t, 201, out.StatusCode()) + + require.NoError(t, err) + + // POST avatar + out, err = client.R().SetAuthToken(token). + SetMultipartField("file", "filename", "image/png", bytes.NewReader(picture)). + SetHeader("x-zitadel-orgid", orgID). + Post("http://localhost:8082" + "/assets/v1" + "/users/me/avatar") + require.NoError(t, err) + require.Equal(t, 200, out.StatusCode()) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition("tester"), + )), + ) + require.NoError(t, err) + + require.NotEmpty(t, user.Human.AvatarKey) + + avatarUrl := "users/tester/avatar?" + assert.Equal(t, avatarUrl, string(user.Human.AvatarKey)[:len(avatarUrl)]) + }, retryDuration, tick) + + // delete avatar + before := time.Now() + out, err = client.R().SetAuthToken(token). + SetHeader("x-zitadel-orgid", orgID). + Delete("http://localhost:8082/auth/v1/users/me/avatar") + require.NoError(t, err) + require.Equal(t, 200, out.StatusCode()) + after := time.Now() + + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition("tester"), + )), + ) + require.NoError(t, err) + + assert.Empty(t, user.Human.AvatarKey) + assert.WithinRange(t, user.UpdatedAt, before, after) + }, retryDuration, tick) + }) + + t.Run("test human user password change reduced", func(t *testing.T) { + createUserReq := &v2_user.AddHumanUserRequest{ + // UserId: gu.Ptr("tester"), + Organization: &object.Organization{ + Org: &object.Organization_OrgId{ + OrgId: orgID, + }, + }, + Profile: &v2_user.SetHumanProfile{ + GivenName: "Donald", + FamilyName: "Duck", + NickName: gu.Ptr("Dukkie"), + DisplayName: gu.Ptr("Donald Duck"), + PreferredLanguage: gu.Ptr("en"), + Gender: v2_user.Gender_GENDER_DIVERSE.Enum(), + }, + Email: &v2_user.SetHumanEmail{ + Email: gofakeit.Email(), + }, + Phone: &v2_user.SetHumanPhone{}, + Metadata: []*v2_user.SetMetadataEntry{ + { + Key: "somekey", + Value: []byte("somevalue"), + }, + }, + PasswordType: &v2_user.AddHumanUserRequest_Password{ + Password: &v2_user.Password{ + Password: "Password1!", + ChangeRequired: true, + }, + }, + } + res, err := UserClient.AddHumanUser(IAMCTX, createUserReq) + require.NoError(t, err) + + userID := res.UserId + + // make sure user exists + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(userID), + )), + ) + require.NoError(t, err) + + assert.NotNil(t, user) + }, retryDuration, tick) + + // change user password + before := time.Now() + _, err = UserClient.UpdateHumanUser(IAMCTX, &v2_user.UpdateHumanUserRequest{ + UserId: userID, + Password: &v2_user.SetPassword{ + PasswordType: &v2_user.SetPassword_Password{ + Password: &v2_user.Password{ + Password: "Password2!", + ChangeRequired: true, + }, + }, + Verification: &v2_user.SetPassword_CurrentPassword{ + CurrentPassword: "Password1!", + }, + }, + }) + require.NoError(t, err) + after := time.Now() + + // check password updated + retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + humanUser, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(userID), + )), + ) + require.NoError(t, err) + + assert.Equal(t, true, humanUser.Human.Password.IsChangeRequired) + assert.WithinRange(t, humanUser.Human.Password.ChangedAt, before, after) + }, retryDuration, tick) + }) +} + +func TestServer_TestMachineUserReduces(t *testing.T) { + instanceID := Instance.ID() + orgID := Instance.DefaultOrg.Id + + userRepo := repository.UserRepository() + + t.Run("test machine user add reduced", func(t *testing.T) { + machineUserRequest := &management.AddMachineUserRequest{ + UserId: gu.Ptr(gofakeit.Name()), + UserName: gofakeit.Name(), + Name: gofakeit.Name(), + Description: gofakeit.Blurb(), + AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_BEARER, + } + + before := time.Now() + resp, err := MgmtClient.AddMachineUser(IAMCTX, machineUserRequest) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(resp.UserId), + )), + ) + require.NoError(t, err) + + // event user.machine.added + // user + assert.Equal(t, instanceID, user.InstanceID) + assert.Equal(t, orgID, user.OrganizationID) + assert.Equal(t, *machineUserRequest.UserId, user.ID) + assert.Equal(t, machineUserRequest.UserName, user.Username) + assert.Equal(t, domain.UserStateActive, user.State) + assert.WithinRange(t, user.UpdatedAt, before, after) + assert.WithinRange(t, user.CreatedAt, before, after) + // machine + assert.Equal(t, machineUserRequest.Name, user.Machine.Name) + assert.Equal(t, machineUserRequest.Description, user.Machine.Description) + assert.Equal(t, domain.AccessTokenTypeBearer, user.Machine.AccessTokenType) + }, retryDuration, tick) + }) + + t.Run("test machine user change reduced", func(t *testing.T) { + userID := gofakeit.Name() + machineUserRequest := &management.AddMachineUserRequest{ + UserId: &userID, + UserName: gofakeit.Name(), + Name: gofakeit.Name(), + Description: gofakeit.Blurb(), + AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_BEARER, + } + + _, err := MgmtClient.AddMachineUser(IAMCTX, machineUserRequest) + require.NoError(t, err) + + updateMachineUserReq := &management.UpdateMachineRequest{ + UserId: userID, + Name: gofakeit.Name(), + Description: gofakeit.Blurb(), + AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT, + } + + before := time.Now() + _, err = MgmtClient.UpdateMachine(IAMCTX, updateMachineUserReq) + require.NoError(t, err) + after := time.Now() + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*15) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + user, err := userRepo.Get( + CTX, + pool, + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.OrganizationIDCondition(orgID), + userRepo.IDCondition(userID), + )), + ) + require.NoError(t, err) + + // event user.machine.changed + // user + assert.WithinRange(t, user.UpdatedAt, before, after) + // machine + assert.Equal(t, updateMachineUserReq.Name, user.Machine.Name) + assert.Equal(t, updateMachineUserReq.Description, user.Machine.Description) + assert.Equal(t, domain.AccessTokenTypeJWT, user.Machine.AccessTokenType) + }, retryDuration, tick, "event not reduced in time for user %q", userID) + t.Log("hodor") + }) +} diff --git a/backend/v3/storage/database/function.go b/backend/v3/storage/database/function.go new file mode 100644 index 000000000..e8e52d004 --- /dev/null +++ b/backend/v3/storage/database/function.go @@ -0,0 +1,112 @@ +package database + +type coalesce struct { + values []any +} + +func Coalesce(values ...any) *coalesce { + return &coalesce{values: values} +} + +// WriteArg implements [Change]. +func (c *coalesce) WriteArg(builder *StatementBuilder) { + c.WriteQualified(builder) +} + +// IsOnColumn implements [Change]. +func (c coalesce) IsOnColumn(col Column) bool { + for _, value := range c.values { + if change, ok := value.(Change); ok { + if change.IsOnColumn(col) { + return true + } + } + } + return false +} + +// Write implements [Change]. +func (c coalesce) Write(builder *StatementBuilder) error { + builder.WriteString("COALESCE(") + for i, value := range c.values { + if i > 0 { + builder.WriteString(", ") + } + builder.WriteArg(value) + } + builder.WriteString(")") + return nil +} + +// Equals implements [Column]. +func (c *coalesce) Equals(col Column) bool { + if col == nil { + return c == nil + } + other, ok := col.(*coalesce) + if !ok || len(other.values) != len(c.values) { + return false + } + for i, value := range c.values { + if value != other.values[i] { + return false + } + } + return true +} + +// Matches implements [Column]. +func (c *coalesce) Matches(x any) bool { + toMatch, ok := x.(*coalesce) + if !ok || len(toMatch.values) != len(c.values) { + return false + } + for i, value := range c.values { + if value != toMatch.values[i] { + return false + } + } + return true +} + +// String implements [Column]. +func (c *coalesce) String() string { + return "database.coalesce" +} + +// WriteQualified implements [Column]. +func (c *coalesce) WriteQualified(builder *StatementBuilder) { + builder.WriteString("COALESCE(") + for i, value := range c.values { + if i > 0 { + builder.WriteString(", ") + } + if col, ok := value.(Column); ok { + col.WriteQualified(builder) + continue + } + builder.WriteArg(value) + } + builder.WriteString(")") +} + +// WriteUnqualified implements [Column]. +func (c *coalesce) WriteUnqualified(builder *StatementBuilder) { + builder.WriteString("COALESCE(") + for i, value := range c.values { + if i > 0 { + builder.WriteString(", ") + } + if col, ok := value.(Column); ok { + col.WriteUnqualified(builder) + continue + } + builder.WriteArg(value) + } + builder.WriteString(")") +} + +var ( + _ Column = (*coalesce)(nil) + _ Change = (*coalesce)(nil) +) diff --git a/backend/v3/storage/database/json/json.go b/backend/v3/storage/database/json/json.go index 55942e6e4..305e47d9d 100644 --- a/backend/v3/storage/database/json/json.go +++ b/backend/v3/storage/database/json/json.go @@ -176,6 +176,7 @@ func NewJsonChanges(col database.Column, changes ...JsonUpdate) database.Change } } +// WriteArg implements [gomock.Matcher]. func (c *jsonChanges) Matches(x any) bool { toMatch, ok := x.(*jsonChanges) if !ok { @@ -205,10 +206,12 @@ func (c *jsonChanges) Matches(x any) bool { return true } +// WriteArg implements [database.Change]. func (c *jsonChanges) String() string { return "database.json.jsonChange" } +// WriteArg implements [database.Change]. func (c jsonChanges) Write(builder *database.StatementBuilder) error { c.column.WriteUnqualified(builder) builder.WriteString(" = ") @@ -220,6 +223,12 @@ func (c jsonChanges) Write(builder *database.StatementBuilder) error { return c.changes[len(c.changes)-1].writeUpdate(builder, c, len(c.changes)-2) } +// WriteArg implements [database.Change]. func (c jsonChanges) IsOnColumn(col database.Column) bool { return c.column.Equals(col) } + +// WriteArg implements [database.Change]. +func (c jsonChanges) WriteArg(builder *database.StatementBuilder) { + c.column.WriteArg(builder) +} diff --git a/backend/v3/storage/database/operators.go b/backend/v3/storage/database/operators.go index c07a08bb9..4fc60836d 100644 --- a/backend/v3/storage/database/operators.go +++ b/backend/v3/storage/database/operators.go @@ -31,7 +31,7 @@ func (b wrappedValue[V]) WriteArg(builder *StatementBuilder) { var _ argWriter = (*wrappedValue[string])(nil) type Value interface { - Boolean | Number | Text | Instruction | Bytes + Boolean | Number | Text | Instruction | Bytes | any } //go:generate enumer -type NumberOperation,TextOperation,BytesOperation -linecomment -output ./operators_enumer.go diff --git a/backend/v3/storage/database/repository/authorization.go b/backend/v3/storage/database/repository/authorization.go index f6864c412..c309a913f 100644 --- a/backend/v3/storage/database/repository/authorization.go +++ b/backend/v3/storage/database/repository/authorization.go @@ -36,17 +36,6 @@ func (a authorization) qualifiedAuthorizationRolesTableName() string { // repository // ------------------------------------------------------------- -const insertAuthorizationStmt = `INSERT INTO zitadel.authorizations ( - instance_id, id, user_id, grant_id, project_id, state, created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) -RETURNING created_at, updated_at` - -const insertAuthorizationWithRolesStmt = `WITH roles AS ( - INSERT INTO zitadel.authorization_roles - (instance_id, authorization_id, grant_id, project_id, role_key) - VALUES ($1, $2, $4, $5, unnest($9::text[])))` + insertAuthorizationStmt - // Create implements [domain.AuthorizationRepository]. func (a authorization) Create(ctx context.Context, client database.QueryExecutor, authorization *domain.Authorization) error { var ( @@ -59,17 +48,12 @@ func (a authorization) Create(ctx context.Context, client database.QueryExecutor updatedAt = authorization.UpdatedAt } - builder := database.NewStatementBuilder(insertAuthorizationWithRolesStmt, - authorization.InstanceID, - authorization.ID, - authorization.UserID, - authorization.GrantID, - authorization.ProjectID, - authorization.State, - createdAt, - updatedAt, - authorization.Roles, + builder := database.NewStatementBuilder("WITH roles AS (INSERT INTO zitadel.authorization_roles (instance_id, authorization_id, grant_id, project_id, role_key)"+ + " VALUES ($1, $2, $3, $4, unnest($5::text[]))) INSERT INTO zitadel.authorizations (instance_id, id, grant_id, project_id, user_id, state, created_at, updated_at) VALUES ($1, $2, $3, $4, $6, $7, ", + authorization.InstanceID, authorization.ID, authorization.GrantID, authorization.ProjectID, authorization.Roles, authorization.UserID, authorization.State, ) + builder.WriteArgs(createdAt, updatedAt) + builder.WriteString(") RETURNING created_at, updated_at") if err := client.QueryRow( ctx, @@ -191,10 +175,10 @@ func (a authorization) setRoles(ctx context.Context, client database.QueryExecut // set the roles builder.WriteString(updateAuthorizationRoleStmt) - err := database.Changes(changes).Write(builder) - if err != nil { + if err := database.Changes(changes).Write(builder); err != nil { return 0, err } + builder.WriteString(updateAuthorizationRoleStmtWhere) return client.Exec(ctx, builder.String(), builder.Args()...) diff --git a/backend/v3/storage/database/repository/authorization_test.go b/backend/v3/storage/database/repository/authorization_test.go index bb4504caa..02d3c6afe 100644 --- a/backend/v3/storage/database/repository/authorization_test.go +++ b/backend/v3/storage/database/repository/authorization_test.go @@ -29,10 +29,8 @@ func TestCreateAuthorization(t *testing.T) { // create project roles role1 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role1") role2 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role2") - // TODO: uncomment when user table is available - // userID := createUser(t, tx, instanceID, organizationID) - // require.NotNil(t, userID) - userID := integration.ID() + userID := createHumanUser(t, tx, instanceID, organizationID) + require.NotNil(t, userID) authorizationRepo := repository.AuthorizationRepository() @@ -116,19 +114,18 @@ func TestCreateAuthorization(t *testing.T) { }, wantErr: new(database.ForeignKeyError), }, - // TODO: uncomment when user table is available - // { - // name: "non-existent user", - // authorization: &domain.Authorization{ - // ID: integration.ID(), - // UserID: "random-id", - // ProjectID: projectID, - // InstanceID: instanceID, - // State: domain.AuthorizationStateActive, - // Roles: nil, - // }, - // wantErr: new(database.ForeignKeyError), - // }, + { + name: "non-existent user", + authorization: &domain.Authorization{ + ID: integration.ID(), + UserID: "random-id", + ProjectID: projectID, + InstanceID: instanceID, + State: domain.AuthorizationStateActive, + Roles: nil, + }, + wantErr: new(database.ForeignKeyError), + }, { name: "duplicate authorization", authorization: &domain.Authorization{ @@ -265,10 +262,8 @@ func TestGetAuthorization(t *testing.T) { role1 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role1") role2 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role2") - // TODO: uncomment when user table is available - // userID := createUser(t, tx, instanceID, organizationID) - // require.NotNil(t, userID) - userID := integration.ID() + userID := createHumanUser(t, tx, instanceID, organizationID) + require.NotNil(t, userID) // create authorization with roles authorizationRepo := repository.AuthorizationRepository() @@ -458,13 +453,10 @@ func TestListAuthorization(t *testing.T) { project2Role1 := createProjectRole(t, tx, instanceID, organizationID, project2ID, "project2Role1") project2Role2 := createProjectRole(t, tx, instanceID, organizationID, project2ID, "project2Role2") - // TODO: uncomment when user table is available - // user1ID := createUser(t, tx, instanceID, organizationID) - // require.NotNil(t, user1ID) - user1ID := integration.ID() - // user2ID := createUser(t, tx, instanceID, organizationID) - // require.NotNil(t, user2ID) - user2ID := integration.ID() + user1ID := createHumanUser(t, tx, instanceID, organizationID) + require.NotNil(t, user1ID) + user2ID := createHumanUser(t, tx, instanceID, organizationID) + require.NotNil(t, user2ID) // create authorization with roles for user1 for project1 authorizationRepo := repository.AuthorizationRepository() @@ -890,11 +882,12 @@ func TestUpdateAuthorization(t *testing.T) { instanceID := createInstance(t, tx) organizationID := createOrganization(t, tx, instanceID) projectID := createProject(t, tx, instanceID, organizationID) + userID := createHumanUser(t, tx, instanceID, organizationID) authorizationRepo := repository.AuthorizationRepository() activeAuthorization := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -907,7 +900,7 @@ func TestUpdateAuthorization(t *testing.T) { inactiveAuthorization := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateInactive, @@ -1012,10 +1005,12 @@ func TestAuthorizationUpdate_setRoles(t *testing.T) { role5 := createProjectRole(t, tx, instanceID, organizationID, project2ID, "role5") role6 := createProjectRole(t, tx, instanceID, organizationID, project2ID, "role6") + userID := createHumanUser(t, tx, instanceID, organizationID) + authorizationRepo := repository.AuthorizationRepository() authorization1 := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -1028,7 +1023,7 @@ func TestAuthorizationUpdate_setRoles(t *testing.T) { authorization2 := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: project2ID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -1132,11 +1127,12 @@ func TestDeleteAuthorization(t *testing.T) { projectID := createProject(t, tx, instanceID, organizationID) role1 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role1") role2 := createProjectRole(t, tx, instanceID, organizationID, projectID, "role2") + userID := createHumanUser(t, tx, instanceID, organizationID) authorizationRepo := repository.AuthorizationRepository() authorizationWithRoles := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -1149,7 +1145,7 @@ func TestDeleteAuthorization(t *testing.T) { authorizationWithoutRoles := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -1162,7 +1158,7 @@ func TestDeleteAuthorization(t *testing.T) { deletedAuthorization := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, InstanceID: instanceID, State: domain.AuthorizationStateActive, @@ -1182,7 +1178,7 @@ func TestDeleteAuthorization(t *testing.T) { grantID := createProjectGrant(t, tx, instanceID, organizationID, grantedOrganizationID, projectID, []string{role1}) authorizationProjectGrant := &domain.Authorization{ ID: integration.ID(), - UserID: integration.ID(), + UserID: userID, ProjectID: projectID, GrantID: gu.Ptr(grantID), InstanceID: instanceID, diff --git a/backend/v3/storage/database/repository/idp_intent.go b/backend/v3/storage/database/repository/idp_intent.go index 9e617bfa6..7bbf9738e 100644 --- a/backend/v3/storage/database/repository/idp_intent.go +++ b/backend/v3/storage/database/repository/idp_intent.go @@ -2,7 +2,6 @@ package repository import ( "context" - "fmt" "net/url" "time" @@ -95,7 +94,6 @@ func (i idpIntentRepository) Get(ctx context.Context, client database.QueryExecu var builder database.StatementBuilder builder.WriteString(queryIDPIntentStmt) options.Write(&builder) - fmt.Println(builder.String()) return scanIDPIntent(ctx, client, &builder) } diff --git a/backend/v3/storage/database/repository/idp_intent_test.go b/backend/v3/storage/database/repository/idp_intent_test.go index 9227d8ecb..c6e127815 100644 --- a/backend/v3/storage/database/repository/idp_intent_test.go +++ b/backend/v3/storage/database/repository/idp_intent_test.go @@ -315,6 +315,8 @@ func TestUpdateIDPIntent(t *testing.T) { err = idpIntentRepo.Create(t.Context(), tx, &intent2) require.NoError(t, err) + userID := createHumanUser(t, tx, instanceID, orgID) + now := time.Now() tomorrow := now.AddDate(0, 0, 1) tt := []struct { @@ -344,7 +346,7 @@ func TestUpdateIDPIntent(t *testing.T) { idpIntentRepo.SetIDPUser([]byte("update user")), idpIntentRepo.SetIDPUserID("idp user id updated"), idpIntentRepo.SetIDPUsername("idp username updated"), - idpIntentRepo.SetUserID("some-user-id"), + idpIntentRepo.SetUserID(userID), idpIntentRepo.SetIDPAccessToken([]byte(`{"secret": "g1g4 s3cr3t 4cc3ss t0k3n"}`)), idpIntentRepo.SetIDPIDToken("idp id token"), idpIntentRepo.SetSucceededAt(now), @@ -355,7 +357,7 @@ func TestUpdateIDPIntent(t *testing.T) { toReturn.IDPUser = []byte("update user") toReturn.IDPUserID = "idp user id updated" toReturn.IDPUsername = "idp username updated" - toReturn.UserID = "some-user-id" + toReturn.UserID = userID toReturn.IDPAccessToken = []byte(`{"secret": "g1g4 s3cr3t 4cc3ss t0k3n"}`) toReturn.IDPIDToken = "idp id token" toReturn.SucceededAt = &now @@ -373,7 +375,7 @@ func TestUpdateIDPIntent(t *testing.T) { idpIntentRepo.SetIDPUser([]byte("update user")), idpIntentRepo.SetIDPUserID("idp user id updated"), idpIntentRepo.SetIDPUsername("idp username updated"), - idpIntentRepo.SetUserID("some-user-id"), + idpIntentRepo.SetUserID(userID), idpIntentRepo.SetAssertion([]byte(`{"assertion": "val1"}`)), idpIntentRepo.SetSucceededAt(now), idpIntentRepo.SetExpiresAt(tomorrow), @@ -383,7 +385,7 @@ func TestUpdateIDPIntent(t *testing.T) { toReturn.IDPUser = []byte("update user") toReturn.IDPUserID = "idp user id updated" toReturn.IDPUsername = "idp username updated" - toReturn.UserID = "some-user-id" + toReturn.UserID = userID toReturn.Assertion = []byte(`{"assertion": "val1"}`) toReturn.SucceededAt = &now toReturn.ExpiresAt = &tomorrow @@ -400,7 +402,7 @@ func TestUpdateIDPIntent(t *testing.T) { idpIntentRepo.SetIDPUser([]byte("update user")), idpIntentRepo.SetIDPUserID("idp user id updated"), idpIntentRepo.SetIDPUsername("idp username updated"), - idpIntentRepo.SetUserID("some-user-id"), + idpIntentRepo.SetUserID(userID), idpIntentRepo.SetIDPEntryAttributes(marshalledAttrs), idpIntentRepo.SetSucceededAt(now), idpIntentRepo.SetExpiresAt(tomorrow), @@ -410,7 +412,7 @@ func TestUpdateIDPIntent(t *testing.T) { toReturn.IDPUser = []byte("update user") toReturn.IDPUserID = "idp user id updated" toReturn.IDPUsername = "idp username updated" - toReturn.UserID = "some-user-id" + toReturn.UserID = userID toReturn.EntryAttributes = entryAttrs toReturn.SucceededAt = &now toReturn.ExpiresAt = &tomorrow diff --git a/backend/v3/storage/database/repository/metadata_test.go b/backend/v3/storage/database/repository/metadata_test.go new file mode 100644 index 000000000..c7b9706b8 --- /dev/null +++ b/backend/v3/storage/database/repository/metadata_test.go @@ -0,0 +1,33 @@ +package repository_test + +import ( + "slices" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/backend/v3/domain" +) + +func assertMetadata(t *testing.T, expected, gotten []*domain.Metadata) { + t.Helper() + + assert.Len(t, gotten, len(expected), "metadata length mismatch") + for _, exp := range expected { + var actual *domain.Metadata + gotten = slices.DeleteFunc(gotten, func(m *domain.Metadata) bool { + if exp.Key != m.Key || exp.InstanceID != m.InstanceID { + return false + } + actual = m + return true + }) + require.NotNil(t, actual, "metadata with key %s and instance %s not found", exp.Key, exp.InstanceID) + assert.Equal(t, exp.Value, actual.Value, "metadata value mismatch for key %s and instance %s", exp.Key, exp.InstanceID) + assert.NotZero(t, actual.CreatedAt, "metadata created at is zero for key %s and instance %s", exp.Key, exp.InstanceID) + assert.NotZero(t, actual.UpdatedAt, "metadata updated at is zero for key %s and instance %s", exp.Key, exp.InstanceID) + + } + assert.Empty(t, gotten, "unmatched metadata found") +} diff --git a/backend/v3/storage/database/repository/project_grant.go b/backend/v3/storage/database/repository/project_grant.go index 0e1106d43..59452e45b 100644 --- a/backend/v3/storage/database/repository/project_grant.go +++ b/backend/v3/storage/database/repository/project_grant.go @@ -123,7 +123,9 @@ func (p projectGrant) Update(ctx context.Context, client database.QueryExecutor, // now we add the logic to do the required changes based on the given project grant builder.WriteString(updateProjectGrantRoleStmt) - database.Changes(changes).Write(builder) + if err := database.Changes(changes).Write(builder); err != nil { + return 0, err + } builder.WriteString(updateProjectGrantRoleStmtWhere) return client.Exec(ctx, builder.String(), builder.Args()...) diff --git a/backend/v3/storage/database/repository/repository.go b/backend/v3/storage/database/repository/repository.go index 345f9a002..fd5ec80b1 100644 --- a/backend/v3/storage/database/repository/repository.go +++ b/backend/v3/storage/database/repository/repository.go @@ -85,7 +85,9 @@ func updateOne[Target updatable](ctx context.Context, client database.QueryExecu builder := database.NewStatementBuilder("UPDATE ") builder.WriteString(target.qualifiedTableName()) builder.WriteString(" SET ") - database.Changes(changes).Write(builder) + if err := database.Changes(changes).Write(builder); err != nil { + return 0, err + } writeCondition(builder, condition) return client.Exec(ctx, builder.String(), builder.Args()...) diff --git a/backend/v3/storage/database/repository/repository_test.go b/backend/v3/storage/database/repository/repository_test.go index 8d26b96f8..9a093a5e5 100644 --- a/backend/v3/storage/database/repository/repository_test.go +++ b/backend/v3/storage/database/repository/repository_test.go @@ -15,6 +15,7 @@ import ( "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres" "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres/embedded" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" "github.com/zitadel/zitadel/internal/integration" @@ -31,30 +32,48 @@ func runTests(m *testing.M) int { var err error ctx := context.Background() pool, stop, err = newEmbeddedDB(ctx) + defer stop() if err != nil { log.Printf("error with embedded postgres database: %v", err) return 1 } - defer stop() + defer func() { + r := recover() + pool.Close(ctx) + stop() + if r != nil { + panic(r) + } + }() return m.Run() } func newEmbeddedDB(ctx context.Context) (pool database.PoolTest, stop func(), err error) { - connector, stop, err := embedded.StartEmbedded() - if err != nil { - return nil, nil, fmt.Errorf("unable to start embedded postgres: %w", err) + var connector database.Connector + if url := os.Getenv("ZITADEL_TEST_POSTGRES_URL"); url != "" { + log.Println("using database provided by env") + connector, err = postgres.DecodeConfig(url) + if err != nil { + return nil, nil, fmt.Errorf("unable to connect to provided postgres: %w", err) + } + stop = func() {} + } else { + connector, stop, err = embedded.StartEmbedded() + if err != nil { + return nil, nil, fmt.Errorf("unable to start embedded postgres: %w", err) + } } pool_, err := connector.Connect(ctx) if err != nil { - return nil, nil, fmt.Errorf("unable to connect to embedded postgres: %w", err) + return nil, stop, fmt.Errorf("unable to connect to embedded postgres: %w", err) } pool = pool_.(database.PoolTest) err = pool.MigrateTest(ctx) if err != nil { - return nil, nil, fmt.Errorf("unable to migrate database: %w", err) + return nil, stop, fmt.Errorf("unable to migrate database: %w", err) } return pool, stop, err } @@ -64,7 +83,8 @@ func transactionForRollback(t *testing.T) (tx database.Transaction, rollback fun tx, err := pool.Begin(t.Context(), nil) require.NoError(t, err) return tx, func() { - err := tx.Rollback(t.Context()) + // context.Background to ensure rollback does not return an error if test is already done + err := tx.Rollback(context.Background()) require.NoError(t, err) } } @@ -74,12 +94,13 @@ func savepointForRollback(t *testing.T, tx database.Transaction) (savepoint data savepoint, err := tx.Begin(t.Context()) require.NoError(t, err) return savepoint, func() { - err := savepoint.Rollback(t.Context()) + // context.Background to ensure rollback does not return an error if test is already done + err := savepoint.Rollback(context.Background()) require.NoError(t, err) } } -func createInstance(t *testing.T, tx database.Transaction) (instanceID string) { +func createInstance(t *testing.T, tx database.QueryExecutor) (instanceID string) { t.Helper() instance := domain.Instance{ ID: gofakeit.UUID(), @@ -97,7 +118,7 @@ func createInstance(t *testing.T, tx database.Transaction) (instanceID string) { return instance.ID } -func createOrganization(t *testing.T, tx database.Transaction, instanceID string) (orgID string) { +func createOrganization(t *testing.T, tx database.QueryExecutor, instanceID string) (orgID string) { t.Helper() org := domain.Organization{ InstanceID: instanceID, @@ -112,7 +133,7 @@ func createOrganization(t *testing.T, tx database.Transaction, instanceID string return org.ID } -func createProject(t *testing.T, tx database.Transaction, instanceID, orgID string) (projectID string) { +func createProject(t *testing.T, tx database.QueryExecutor, instanceID, orgID string) (projectID string) { t.Helper() project := domain.Project{ InstanceID: instanceID, @@ -128,7 +149,7 @@ func createProject(t *testing.T, tx database.Transaction, instanceID, orgID stri return project.ID } -func createProjectRole(t *testing.T, tx database.Transaction, instanceID, orgID, projectID, key string) string { +func createProjectRole(t *testing.T, tx database.QueryExecutor, instanceID, orgID, projectID, key string) string { t.Helper() if key == "" { key = integration.RoleKey() @@ -210,3 +231,46 @@ func createIDPIntent(t *testing.T, tx database.Transaction, instanceID, idpID st return intent.ID } + +func createMachineUser(t *testing.T, tx database.QueryExecutor, instanceID, orgID string) (userID string) { + t.Helper() + user := domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: gofakeit.UUID(), + Username: gofakeit.Username(), + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: gofakeit.Name(), + }, + } + userRepo := repository.UserRepository() + err := userRepo.Create(t.Context(), tx, &user) + require.NoError(t, err) + return user.ID +} + +func createHumanUser(t *testing.T, tx database.QueryExecutor, instanceID, orgID string) string { + t.Helper() + user := &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: gofakeit.UUID(), + Username: gofakeit.Username(), + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: gofakeit.FirstName(), + LastName: gofakeit.LastName(), + Email: domain.HumanEmail{ + Address: gofakeit.Email(), + }, + Password: domain.HumanPassword{ + Hash: "hashed-password", + }, + }, + } + userRepo := repository.UserRepository() + err := userRepo.Create(t.Context(), tx, user) + require.NoError(t, err) + return user.ID +} diff --git a/backend/v3/storage/database/repository/session.go b/backend/v3/storage/database/repository/session.go index 47e21cce1..be7d629d2 100644 --- a/backend/v3/storage/database/repository/session.go +++ b/backend/v3/storage/database/repository/session.go @@ -176,7 +176,9 @@ func (s session) Create(ctx context.Context, client database.QueryExecutor, sess ) fingerprintID = session.UserAgent.FingerprintID } - builder.WriteString(`INSERT INTO ` + s.qualifiedTableName() + ` (instance_id, id, lifetime, creator_id, user_agent_id, created_at, updated_at) VALUES ( `) + builder.WriteString(`INSERT INTO `) + builder.WriteString(s.qualifiedTableName()) + builder.WriteString(` (instance_id, id, lifetime, creator_id, user_agent_id, created_at, updated_at) VALUES ( `) builder.WriteArgs(session.InstanceID, session.ID, session.Lifetime, session.CreatorID, fingerprintID, createdAt, updatedAt) builder.WriteString(` ) RETURNING created_at, updated_at`) return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&session.CreatedAt, &session.UpdatedAt) diff --git a/backend/v3/storage/database/repository/session_test.go b/backend/v3/storage/database/repository/session_test.go index 4ef03d41e..05f5db94c 100644 --- a/backend/v3/storage/database/repository/session_test.go +++ b/backend/v3/storage/database/repository/session_test.go @@ -232,6 +232,9 @@ func TestSession_Update(t *testing.T) { err = instanceRepo.Create(t.Context(), tx, &instance) require.NoError(t, err) + orgID := createOrganization(t, tx, instanceId) + userID := createHumanUser(t, tx, instanceId, orgID) + testNow := time.Now() tests := []struct { name string @@ -474,7 +477,7 @@ func TestSession_Update(t *testing.T) { // update with updated values session.Factors = []domain.SessionFactor{ &domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }, } @@ -482,7 +485,7 @@ func TestSession_Update(t *testing.T) { }, update: []database.Change{ sessionRepo.SetFactor(&domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }), }, @@ -748,7 +751,7 @@ func TestSession_Update(t *testing.T) { session.TokenID = "new-token" session.Factors = []domain.SessionFactor{ &domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }, &domain.SessionFactorPassword{ @@ -794,7 +797,7 @@ func TestSession_Update(t *testing.T) { sessionRepo.SetUpdatedAt(testNow), sessionRepo.SetLifetime(time.Hour * 48), sessionRepo.SetFactor(&domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }), sessionRepo.SetFactor(&domain.SessionFactorPassword{ @@ -846,7 +849,7 @@ func TestSession_Update(t *testing.T) { updatedRows, err := sessionRepo.Update(t.Context(), tx, sessionRepo.PrimaryKeyCondition(session.InstanceID, session.ID), sessionRepo.SetFactor(&domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }), sessionRepo.SetFactor(&domain.SessionFactorPassword{ @@ -859,7 +862,7 @@ func TestSession_Update(t *testing.T) { // update with updated values session.Factors = []domain.SessionFactor{ &domain.SessionFactorUser{ - UserID: "user-id", + UserID: userID, LastVerifiedAt: testNow, }, } @@ -1101,9 +1104,12 @@ func TestSession_List(t *testing.T) { err = instanceRepo.Create(t.Context(), tx, &instance) require.NoError(t, err) + orgID := createOrganization(t, tx, instanceId) + userIDs := []string{createHumanUser(t, tx, instanceId, orgID), createHumanUser(t, tx, instanceId, orgID)} + // create sessions sessions := make([]*domain.Session, 5) - for i := 0; i < 5; i++ { + for i := range 5 { session := &domain.Session{ InstanceID: instanceId, ID: strconv.Itoa(i), @@ -1138,7 +1144,7 @@ func TestSession_List(t *testing.T) { session.Metadata = metadata changes = append(changes, sessionRepo.SetMetadata(metadata)) userFactor := &domain.SessionFactorUser{ - UserID: "user-" + strconv.Itoa(i%2), // two users: user-0 and user-1 + UserID: userIDs[i%2], LastVerifiedAt: time.Now(), } session.Factors.AppendTo(userFactor) diff --git a/backend/v3/storage/database/repository/user.go b/backend/v3/storage/database/repository/user.go new file mode 100644 index 000000000..d560683f1 --- /dev/null +++ b/backend/v3/storage/database/repository/user.go @@ -0,0 +1,479 @@ +package repository + +import ( + "context" + "encoding/json" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/internal/zerrors" +) + +// ------------------------------------------------------------- +// repository +// ------------------------------------------------------------- + +func UserRepository() domain.UserRepository { + return new(user) +} + +func MachineUserRepository() domain.MachineUserRepository { + return &userMachine{user: user{}} +} + +type user struct { + verification verification + tableName string + machineKey + userPersonalAccessToken + userMetadata + userPasskey + userIdentityProviderLink +} + +func (u user) HumanRepository() domain.HumanUserRepository { + return userHuman{user: u} +} + +// existingUser is used to get the columns and conditions for the CTE that selects the existing user in update and delete operations. +var existingUser = user{tableName: "existing_user"} + +// Create implements [domain.UserRepository]. +func (u user) Create(ctx context.Context, client database.QueryExecutor, user *domain.User) error { + var create func(context.Context, *database.StatementBuilder, database.QueryExecutor, *domain.User) error + switch { + case user.Human != nil: + create = userHuman{user: u}.create + case user.Machine != nil: + create = userMachine{user: u}.create + default: + return zerrors.ThrowInvalidArgument(nil, "REPOS-KxMnG", "no user type defined") + } + // we need to cheat a bit here to be able to use CTEChanges + // we pretend we have an existing user to be able to use the existing change mechanisms + builder := database.NewStatementBuilder("WITH existing_user AS (SELECT $1 AS instance_id, $2 AS organization_id, $3 AS id)", user.InstanceID, user.OrganizationID, user.ID) + return create(ctx, builder, client, user) +} + +// Delete implements [domain.UserRepository]. +func (u user) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) { + if err := checkPKCondition(u, condition); err != nil { + return 0, err + } + + builder := database.NewStatementBuilder("DELETE FROM zitadel.users") + writeCondition(builder, condition) + return client.Exec(ctx, builder.String(), builder.Args()...) +} + +var queryUserStmt = "SELECT users.instance_id, users.organization_id, users.id, users.username" + + ", users.state, users.created_at, users.updated_at" + + // metadata + `, jsonb_agg(DISTINCT jsonb_build_object('instanceId', user_metadata.instance_id, 'key', user_metadata.key, 'value', encode(user_metadata.value, 'base64'), 'createdAt', user_metadata.created_at, 'updatedAt', user_metadata.updated_at)) FILTER (WHERE user_metadata.user_id IS NOT NULL) AS metadata` + + // machine + `, CASE WHEN users.type = 'machine' THEN jsonb_build_object('name', users.name` + + `, 'description', users.description, 'secret', users.secret` + + `, 'accessTokenType', users.access_token_type` + + `, 'keys', jsonb_agg(DISTINCT jsonb_build_object('id', machine_keys.id, 'publicKey', encode(machine_keys.public_key, 'base64'), 'createdAt', machine_keys.created_at, 'expiresAt', machine_keys.expires_at, 'type', machine_keys.type)) FILTER (WHERE machine_keys.user_id IS NOT NULL)` + + `, 'pats', jsonb_agg(DISTINCT jsonb_build_object('id', user_personal_access_tokens.id, 'createdAt', user_personal_access_tokens.created_at, 'expiresAt', user_personal_access_tokens.expires_at, 'scopes', user_personal_access_tokens.scopes)) FILTER (WHERE user_personal_access_tokens.user_id IS NOT NULL)` + + `) END AS machine` + + // human + `, CASE WHEN users.type = 'human' THEN jsonb_build_object('firstName', users.first_name, 'lastName', users.last_name` + + `, 'nickname', users.nickname, 'displayName', users.display_name` + + `, 'preferredLanguage', users.preferred_language, 'gender', users.gender` + + `, 'avatarKey', users.avatar_key` + + `, 'multifactorInitializationSkippedAt', users.multifactor_initialization_skipped_at` + + `, 'password', jsonb_build_object('hash', users.password_hash, 'isChangeRequired', users.password_change_required, 'changedAt', users.password_changed_at, 'failedAttempts', users.password_failed_attempts, 'lastSuccessfullyCheckedAt', users.password_last_successful_check, 'pendingVerification', ` + verificationQuery(userHuman{}.passwordVerificationIDColumn()) + `)` + + `, 'email', jsonb_build_object('address', users.email, 'unverifiedAddress', users.unverified_email, 'verifiedAt', users.email_verified_at, 'otp', jsonb_build_object('enabledAt', users.email_otp_enabled_at, 'lastSuccessfullyCheckedAt', users.email_otp_last_successful_check, 'failedAttempts', users.email_otp_failed_attempts), 'pendingVerification', ` + verificationQuery(userHuman{}.emailVerificationIDColumn()) + `)` + + `, 'phone', CASE WHEN users.phone IS NOT NULL OR users.phone_verification_id IS NOT NULL THEN jsonb_build_object('number', users.phone, 'unverifiedNumber', users.unverified_phone, 'verifiedAt', users.phone_verified_at, 'otp', jsonb_build_object('enabledAt', users.sms_otp_enabled_at, 'lastSuccessfullyCheckedAt', users.sms_otp_last_successful_check, 'failedAttempts', users.sms_otp_failed_attempts), 'pendingVerification', ` + verificationQuery(userHuman{}.phoneVerificationIDColumn()) + `) ELSE NULL END` + + `, 'totp', CASE WHEN users.totp_secret IS NOT NULL THEN jsonb_build_object('secret', encode(users.totp_secret, 'escape')::JSONB, 'verifiedAt', users.totp_verified_at, 'lastSuccessfullyCheckedAt', users.totp_last_successful_check, 'failedAttempts', users.totp_failed_attempts) ELSE NULL END` + + `, 'passkeys', jsonb_agg(DISTINCT jsonb_build_object('id', user_passkeys.token_id, 'keyId', encode(user_passkeys.key_id, 'base64'), 'type', user_passkeys.type, 'name', user_passkeys.name, 'signCount', user_passkeys.sign_count, 'challenge', encode(user_passkeys.challenge, 'base64'), 'publicKey', encode(user_passkeys.public_key, 'base64'), 'attestationType', user_passkeys.attestation_type, 'aaGuid', encode(user_passkeys.authenticator_attestation_guid, 'base64'), 'rpId', user_passkeys.relying_party_id, 'createdAt', user_passkeys.created_at, 'updatedAt', user_passkeys.updated_at, 'verifiedAt', user_passkeys.verified_at)) FILTER (WHERE user_passkeys.user_id IS NOT NULL)` + + `, 'verifications', (SELECT jsonb_agg(jsonb_build_object('id', verifications.id, 'code', encode(verifications.code, 'escape')::JSONB, 'createdAt', verifications.created_at, 'updatedAt', verifications.updated_at, 'expiresAt', verifications.created_at+verifications.expiry, 'failedAttempts', verifications.failed_attempts)) FROM zitadel.verifications WHERE verifications.instance_id = users.instance_id AND verifications.user_id = users.id AND verifications.id NOT IN (COALESCE(users.password_verification_id, ''), COALESCE(users.email_verification_id, ''), COALESCE(users.phone_verification_id, ''), COALESCE(users.invite_verification_id, '')))` + + `, 'identityProviderLinks', jsonb_agg(DISTINCT jsonb_build_object('providerId', user_identity_provider_links.identity_provider_id, 'providedUserId', user_identity_provider_links.provided_user_id, 'providedUsername', user_identity_provider_links.provided_username, 'createdAt', user_identity_provider_links.created_at, 'updatedAt', user_identity_provider_links.updated_at)) FILTER (WHERE user_identity_provider_links.user_id IS NOT NULL)` + + `, 'invite', CASE WHEN users.invite_verification_id IS NOT NULL OR users.invite_accepted_at IS NOT NULL THEN jsonb_build_object('acceptedAt', users.invite_accepted_at, 'failedAttempts', users.invite_failed_attempts, 'pendingVerification', ` + verificationQuery(userHuman{}.inviteVerificationIDColumn()) + `) ELSE NULL END` + + `) END AS human FROM zitadel.users` + +func verificationQuery(column database.Column) string { + var builder database.StatementBuilder + builder.WriteString("CASE WHEN ") + column.WriteQualified(&builder) + builder.WriteString(` IS NOT NULL THEN (SELECT row_to_json(res.*) FROM (SELECT verifications.id, encode(verifications.code, 'escape')::JSONB AS code, verifications.created_at as "createdAt", verifications.updated_at as "updatedAt", verifications.created_at+verifications.expiry AS "expiresAt", verifications.failed_attempts as "failedAttempts" FROM zitadel.verifications WHERE verifications.instance_id = users.instance_id AND verifications.id = `) + column.WriteQualified(&builder) + builder.WriteString(`) AS res) ELSE NULL END`) + return builder.String() +} + +// Get implements [domain.UserRepository]. +func (u user) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.User, error) { + options := new(database.QueryOpts) + for _, opt := range u.appendQueryOpts(opts) { + opt(options) + } + + if !options.Condition.IsRestrictingColumn(u.InstanceIDColumn()) { + return nil, database.NewMissingConditionError(u.InstanceIDColumn()) + } + + builder := database.NewStatementBuilder(queryUserStmt) + options.Write(builder) + + return scanUser(ctx, client, builder) +} + +// List implements [domain.UserRepository]. +func (u user) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.User, error) { + options := new(database.QueryOpts) + for _, opt := range u.appendQueryOpts(opts) { + opt(options) + } + + if !options.Condition.IsRestrictingColumn(u.InstanceIDColumn()) { + return nil, database.NewMissingConditionError(u.InstanceIDColumn()) + } + + builder := database.NewStatementBuilder(queryUserStmt) + options.Write(builder) + + return scanUsers(ctx, client, builder) +} + +func (u user) appendQueryOpts(opts []database.QueryOption) []database.QueryOption { + return append(opts, + u.joinMachineKeys(), + u.joinPATs(), + u.joinMetadata(), + u.joinVerifications(), + u.joinPasskeys(), + u.joinIdentityProviderLinks(), + database.WithGroupBy(u.PrimaryKeyColumns()...), + ) +} + +// Update implements [domain.UserRepository]. +func (u user) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { + if err := checkPKCondition(u, condition); err != nil { + return 0, err + } + if len(changes) == 0 { + return 0, database.ErrNoChanges + } + if !database.Changes(changes).IsOnColumn(u.updatedAtColumn()) { + changes = append(changes, u.clearUpdatedAt()) + } + + builder := database.NewStatementBuilder("WITH existing_user AS (SELECT * FROM zitadel.users") + writeCondition(builder, condition) + builder.WriteString(") ") + for i, change := range changes { + sessionCTE(change, i, 0, builder) + } + + builder.WriteString("UPDATE zitadel.users SET ") + if err := database.Changes(changes).Write(builder); err != nil { + return 0, err + } + + builder.WriteString(" FROM existing_user") + writeCondition(builder, database.And( + database.NewColumnCondition(u.IDColumn(), existingUser.IDColumn()), + database.NewColumnCondition(u.InstanceIDColumn(), existingUser.InstanceIDColumn()), + )) + + return client.Exec(ctx, builder.String(), builder.Args()...) +} + +func (u user) unqualifiedTableName() string { + if u.tableName != "" { + return u.tableName + } + return "users" +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetState implements [domain.UserRepository]. +func (u user) SetState(state domain.UserState) database.Change { + return database.NewChange(u.StateColumn(), state) +} + +// SetUpdatedAt implements [domain.UserRepository]. +func (u user) SetUpdatedAt(updatedAt time.Time) database.Change { + return database.NewChange(u.updatedAtColumn(), updatedAt) +} + +// SetUsername implements [domain.UserRepository]. +func (u user) SetUsername(username string) database.Change { + return database.NewChange(u.UsernameColumn(), username) +} + +func (u user) clearUpdatedAt() database.Change { + return database.NewChange(u.updatedAtColumn(), database.NullInstruction) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// IDCondition implements [domain.UserRepository]. +func (u user) IDCondition(userID string) database.Condition { + return database.NewTextCondition(u.IDColumn(), database.TextOperationEqual, userID) +} + +// InstanceIDCondition implements [domain.UserRepository]. +func (u user) InstanceIDCondition(instanceID string) database.Condition { + return database.NewTextCondition(u.InstanceIDColumn(), database.TextOperationEqual, instanceID) +} + +// LoginNameCondition implements [domain.UserRepository]. +func (u user) LoginNameCondition(op database.TextOperation, loginName string) database.Condition { + panic("unimplemented") +} + +// OrganizationIDCondition implements [domain.UserRepository]. +func (u user) OrganizationIDCondition(orgID string) database.Condition { + return database.NewTextCondition(u.organizationIDColumn(), database.TextOperationEqual, orgID) +} + +// PrimaryKeyCondition implements [domain.UserRepository]. +func (u user) PrimaryKeyCondition(instanceID string, userID string) database.Condition { + return database.And( + u.InstanceIDCondition(instanceID), + u.IDCondition(userID), + ) +} + +// StateCondition implements [domain.UserRepository]. +func (u user) StateCondition(state domain.UserState) database.Condition { + return database.NewNumberCondition(u.StateColumn(), database.NumberOperationEqual, state) +} + +// TypeCondition implements [domain.UserRepository]. +func (u user) TypeCondition(userType domain.UserType) database.Condition { + return database.NewNumberCondition(u.TypeColumn(), database.NumberOperationEqual, userType) +} + +// UsernameCondition implements [domain.UserRepository]. +func (u user) UsernameCondition(op database.TextOperation, username string) database.Condition { + return database.NewTextCondition(u.UsernameColumn(), op, username) +} + +// ExistsMetadata implements [domain.UserRepository]. +func (u user) ExistsMetadata(condition database.Condition) database.Condition { + return database.Exists( + u.userMetadata.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userMetadata.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userMetadata.userIDColumn()), + condition, + ), + ) +} + +// ExistsPasskey implements [domain.HumanUserRepository]. +func (u user) ExistsPasskey(condition database.Condition) database.Condition { + return database.Exists( + u.userPasskey.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userPasskey.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userPasskey.userIDColumn()), + condition, + ), + ) +} + +// ExistsIdentityProviderLink implements [domain.HumanUserRepository]. +func (u user) ExistsIdentityProviderLink(condition database.Condition) database.Condition { + return database.Exists( + u.userIdentityProviderLink.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userIdentityProviderLink.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userIdentityProviderLink.userIDColumn()), + condition, + ), + ) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +// PrimaryKeyColumns implements [domain.UserRepository]. +func (u user) PrimaryKeyColumns() []database.Column { + return database.Columns{ + u.InstanceIDColumn(), + u.IDColumn(), + } +} + +// CreatedAtColumn implements [domain.UserRepository]. +func (u user) CreatedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "created_at") +} + +// StateColumn implements [domain.UserRepository]. +func (u user) StateColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "state") +} + +// TypeColumn implements [domain.UserRepository]. +func (u user) TypeColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "type") +} + +// UsernameColumn implements [domain.UserRepository]. +func (u user) UsernameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "username") +} + +// IDColumn implements [domain.UserRepository]. +func (u user) IDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "id") +} + +func (u user) InstanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} + +func (u user) updatedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "updated_at") +} + +func (u user) organizationIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "organization_id") +} + +// ------------------------------------------------------------- +// scanners +// ------------------------------------------------------------- + +type rawUser struct { + domain.User + Machine *json.RawMessage `json:"machine,omitempty" db:"machine"` + Human *json.RawMessage `json:"human,omitempty" db:"human"` +} + +func (u *rawUser) toDomain() (*domain.User, error) { + if u.Machine != nil { + err := json.Unmarshal(*u.Machine, &u.User.Machine) + if err != nil { + return nil, err + } + } else if u.Human != nil { + err := json.Unmarshal(*u.Human, &u.User.Human) + if err != nil { + return nil, err + } + } + return &u.User, nil +} + +func scanUser(ctx context.Context, client database.QueryExecutor, builder *database.StatementBuilder) (*domain.User, error) { + rows, err := client.Query(ctx, builder.String(), builder.Args()...) + if err != nil { + return nil, err + } + + var user rawUser + if err = rows.(database.CollectableRows).CollectExactlyOneRow(&user); err != nil { + return nil, err + } + return user.toDomain() +} + +func scanUsers(ctx context.Context, client database.QueryExecutor, builder *database.StatementBuilder) ([]*domain.User, error) { + rows, err := client.Query(ctx, builder.String(), builder.Args()...) + if err != nil { + return nil, err + } + + var users []rawUser + if err = rows.(database.CollectableRows).Collect(&users); err != nil { + return nil, err + } + result := make([]*domain.User, len(users)) + for i, user := range users { + domainUser, err := user.toDomain() + if err != nil { + return nil, err + } + result[i] = domainUser + } + return result, nil +} + +// ------------------------------------------------------------- +// sub repositories +// ------------------------------------------------------------- + +// Human implements [domain.UserRepository]. +func (u user) Human() domain.HumanUserRepository { + return userHuman{user: u} +} + +// Machine implements [domain.UserRepository]. +func (u user) Machine() domain.MachineUserRepository { + return userMachine{user: u} +} + +func (u user) joinMachineKeys() database.QueryOption { + return database.WithLeftJoin( + u.machineKey.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.machineKey.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.machineKey.userIDColumn()), + ), + ) +} + +func (u user) joinPATs() database.QueryOption { + return database.WithLeftJoin( + u.userPersonalAccessToken.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userPersonalAccessToken.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userPersonalAccessToken.userIDColumn()), + ), + ) +} + +func (u user) joinMetadata() database.QueryOption { + return database.WithLeftJoin( + u.userMetadata.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userMetadata.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userMetadata.userIDColumn()), + ), + ) +} + +func (u user) joinPasskeys() database.QueryOption { + return database.WithLeftJoin( + u.userPasskey.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userPasskey.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userPasskey.userIDColumn()), + ), + ) +} + +func (u user) joinVerifications() database.QueryOption { + return database.WithLeftJoin( + u.verification.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.verification.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.verification.userIDColumn()), + ), + ) +} + +func (u user) joinIdentityProviderLinks() database.QueryOption { + return database.WithLeftJoin( + u.userIdentityProviderLink.qualifiedTableName(), + database.And( + database.NewColumnCondition(u.InstanceIDColumn(), u.userIdentityProviderLink.instanceIDColumn()), + database.NewColumnCondition(u.IDColumn(), u.userIdentityProviderLink.userIDColumn()), + ), + ) +} diff --git a/backend/v3/storage/database/repository/user_human.go b/backend/v3/storage/database/repository/user_human.go new file mode 100644 index 000000000..19c33e974 --- /dev/null +++ b/backend/v3/storage/database/repository/user_human.go @@ -0,0 +1,486 @@ +package repository + +import ( + "context" + "fmt" + "maps" + "slices" + "strings" + "time" + + "github.com/muhlemmer/gu" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/instrumentation/logging" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +// ------------------------------------------------------------- +// repository +// ------------------------------------------------------------- + +type userHuman struct { + user +} + +var existingHumanUser = userHuman{ + user: user{ + tableName: "existing_user", + }, +} + +func HumanUserRepository() domain.HumanUserRepository { + return &userHuman{ + user: user{}, + } +} + +func (u userHuman) create(ctx context.Context, builder *database.StatementBuilder, client database.QueryExecutor, user *domain.User) error { + var createdAt any = database.NowInstruction + if !user.CreatedAt.IsZero() { + createdAt = user.CreatedAt + } + columnValues := map[string]any{ + "instance_id": user.InstanceID, + "organization_id": user.OrganizationID, + "id": user.ID, + "username": user.Username, + "state": user.State, + "type": "human", + "first_name": user.Human.FirstName, + "last_name": user.Human.LastName, + "created_at": createdAt, + "updated_at": createdAt, + } + if !user.Human.MultifactorInitializationSkippedAt.IsZero() { + columnValues["multifactor_initialization_skipped_at"] = user.Human.MultifactorInitializationSkippedAt + } + columnValues = u.setOptionalProfileColumns(columnValues, user.Human) + + ctes := make(map[string]database.CTEChange) + + columnValues, ctes = u.setPasswordColumns(columnValues, ctes, user, createdAt) + columnValues, ctes = u.setEmailColumns(columnValues, ctes, user, createdAt) + columnValues, ctes = u.setPhoneColumns(columnValues, ctes, user, createdAt) + columnValues, ctes = u.setInviteColumns(columnValues, ctes, user, createdAt) + + for i, passkey := range user.Human.Passkeys { + ctes[fmt.Sprintf("passkey_%d", i)] = u.AddPasskey(passkey).(database.CTEChange) + } + + if user.Human.TOTP != nil { + columnValues["totp_secret"] = user.Human.TOTP.Secret + if !user.Human.TOTP.VerifiedAt.IsZero() { + columnValues["totp_verified_at"] = user.Human.TOTP.VerifiedAt + } + } + + for i, link := range user.Human.IdentityProviderLinks { + name := fmt.Sprintf("idp_link_%d", i) + ctes[name] = u.AddIdentityProviderLink(link).(database.CTEChange) + } + + for i, verification := range user.Human.Verifications { + ctes[fmt.Sprintf("verification_%d", i)] = u.SetVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr(verification.ID), + CreatedAt: user.CreatedAt, + Code: verification.Code, + }).(database.CTEChange) + } + + for i, metadata := range user.Metadata { + ctes[fmt.Sprintf("metadata_%d", i)] = u.SetMetadata(metadata).(database.CTEChange) + } + + // write CTE changes + for name, cte := range ctes { + cte.SetName(name) + builder.WriteString(", ") + builder.WriteString(name) + builder.WriteString(" AS (") + cte.WriteCTE(builder) + builder.WriteString(")") + } + columns := slices.Sorted(maps.Keys(columnValues)) + + // write final insert + builder.WriteString(" INSERT INTO zitadel.users (") + builder.WriteString(strings.Join(columns, ", ")) + builder.WriteString(") VALUES (") + for i, column := range columns { + if i > 0 { + builder.WriteString(", ") + } + value := columnValues[column] + if fn, ok := value.(func(builder *database.StatementBuilder)); ok { + fn(builder) + continue + } + builder.WriteArg(value) + } + builder.WriteString(") RETURNING created_at, updated_at") + + return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&user.CreatedAt, &user.UpdatedAt) +} + +func (u userHuman) setPasswordColumns(columnValues map[string]any, ctes map[string]database.CTEChange, user *domain.User, createdAt any) (map[string]any, map[string]database.CTEChange) { + if user.Human.Password.PendingVerification != nil { + verification := &domain.VerificationTypeInit{ + CreatedAt: user.CreatedAt, + Code: user.Human.Password.PendingVerification.Code, + } + if user.Human.Password.PendingVerification.ID != "" { + verification.ID = &user.Human.Password.PendingVerification.ID + } + if user.Human.Password.PendingVerification.ExpiresAt != nil && !user.Human.Password.PendingVerification.ExpiresAt.IsZero() { + verification.Expiry = gu.Ptr(time.Since(*user.Human.Password.PendingVerification.ExpiresAt)) + } + + ctes["password_verification"] = u.SetResetPasswordVerification(verification).(database.CTEChange) + columnValues["password_verification_id"] = func(builder *database.StatementBuilder) { + builder.WriteString(`(SELECT id FROM password_verification)`) + } + } else if user.Human.Password.Hash != "" { + columnValues["password_hash"] = user.Human.Password.Hash + columnValues["password_changed_at"] = createdAt + if !user.Human.Password.ChangedAt.IsZero() { + columnValues["password_changed_at"] = user.Human.Password.ChangedAt + } + } + return columnValues, ctes +} + +func (u userHuman) setEmailColumns(columnValues map[string]any, ctes map[string]database.CTEChange, user *domain.User, createdAt any) (map[string]any, map[string]database.CTEChange) { + if user.Human.Email.PendingVerification != nil { + verification := &domain.VerificationTypeInit{ + CreatedAt: user.CreatedAt, + Code: user.Human.Email.PendingVerification.Code, + } + if user.Human.Email.PendingVerification.ID != "" { + verification.ID = &user.Human.Email.PendingVerification.ID + } + if user.Human.Email.PendingVerification.ExpiresAt != nil && !user.Human.Email.PendingVerification.ExpiresAt.IsZero() { + verification.Expiry = gu.Ptr(time.Since(*user.Human.Email.PendingVerification.ExpiresAt)) + } + + ctes["email_verification"] = u.SetEmailVerification(verification).(database.CTEChange) + columnValues["email_verification_id"] = func(builder *database.StatementBuilder) { + builder.WriteString(`(SELECT id FROM email_verification)`) + } + } + if user.Human.Email.Address != "" { + columnValues["email"] = user.Human.Email.Address + columnValues["unverified_email"] = user.Human.Email.Address + columnValues["email_verified_at"] = createdAt + if !user.Human.Email.VerifiedAt.IsZero() { + columnValues["email_verified_at"] = user.Human.Email.VerifiedAt + } + } + if user.Human.Email.UnverifiedAddress != "" { + columnValues["unverified_email"] = user.Human.Email.UnverifiedAddress + } + return columnValues, ctes +} + +func (u userHuman) setPhoneColumns(columnValues map[string]any, ctes map[string]database.CTEChange, user *domain.User, createdAt any) (map[string]any, map[string]database.CTEChange) { + if user.Human.Phone == nil { + return columnValues, ctes + } + if user.Human.Phone.PendingVerification != nil { + verification := &domain.VerificationTypeInit{ + CreatedAt: user.CreatedAt, + Code: user.Human.Phone.PendingVerification.Code, + } + if user.Human.Phone.PendingVerification.ID != "" { + verification.ID = &user.Human.Phone.PendingVerification.ID + } + if user.Human.Phone.PendingVerification.ExpiresAt != nil && !user.Human.Phone.PendingVerification.ExpiresAt.IsZero() { + verification.Expiry = gu.Ptr(time.Since(*user.Human.Phone.PendingVerification.ExpiresAt)) + } + + ctes["phone_verification"] = u.SetPhoneVerification(verification).(database.CTEChange) + columnValues["phone_verification_id"] = func(builder *database.StatementBuilder) { + builder.WriteString(`(SELECT id FROM phone_verification)`) + } + } + if user.Human.Phone.Number != "" { + columnValues["phone"] = user.Human.Phone.Number + columnValues["unverified_phone"] = user.Human.Phone.Number + columnValues["phone_verified_at"] = createdAt + if !user.Human.Phone.VerifiedAt.IsZero() { + columnValues["phone_verified_at"] = user.Human.Phone.VerifiedAt + } + } + if user.Human.Phone.UnverifiedNumber != "" { + columnValues["unverified_phone"] = user.Human.Phone.UnverifiedNumber + } + return columnValues, ctes +} + +func (u userHuman) setInviteColumns(columnValues map[string]any, ctes map[string]database.CTEChange, user *domain.User, createdAt any) (map[string]any, map[string]database.CTEChange) { + if user.Human.Invite == nil { + return columnValues, ctes + } + if user.Human.Invite.PendingVerification != nil { + verification := &domain.VerificationTypeInit{ + CreatedAt: user.CreatedAt, + Code: user.Human.Invite.PendingVerification.Code, + } + if user.Human.Invite.PendingVerification.ID != "" { + verification.ID = &user.Human.Invite.PendingVerification.ID + } + if user.Human.Invite.PendingVerification.ExpiresAt != nil && !user.Human.Invite.PendingVerification.ExpiresAt.IsZero() { + verification.Expiry = gu.Ptr(time.Since(*user.Human.Invite.PendingVerification.ExpiresAt)) + } + ctes["invite_verification"] = u.SetInviteVerification(verification).(database.CTEChange) + } + if !user.Human.Invite.AcceptedAt.IsZero() { + columnValues["invite_accepted_at"] = createdAt + } + return columnValues, ctes +} + +func (u userHuman) setOptionalProfileColumns(columnValues map[string]any, human *domain.HumanUser) map[string]any { + if !human.PreferredLanguage.IsRoot() { + columnValues["preferred_language"] = human.PreferredLanguage + } + if human.DisplayName != "" { + columnValues["display_name"] = human.DisplayName + } + if human.Nickname != "" { + columnValues["nickname"] = human.Nickname + } + if human.Gender != domain.HumanGenderUnspecified { + columnValues["gender"] = human.Gender + } + if human.AvatarKey != "" { + columnValues["avatar_key"] = human.AvatarKey + } + if human.Password.IsChangeRequired { + columnValues["password_change_required"] = true + } + return columnValues +} + +func (u userHuman) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { + if !condition.IsRestrictingColumn(u.TypeColumn()) { + condition = database.And(condition, u.TypeCondition(domain.UserTypeHuman)) + } + return u.user.Update(ctx, client, condition, changes...) +} + +var _ domain.HumanUserRepository = (*userHuman)(nil) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetAvatarKey implements [domain.HumanUserRepository]. +func (u userHuman) SetAvatarKey(avatarKey *string) database.Change { + return database.NewChangePtr(u.avatarKeyColumn(), avatarKey) +} + +// SetDisplayName implements [domain.HumanUserRepository]. +func (u userHuman) SetDisplayName(displayName string) database.Change { + return database.NewChange(u.DisplayNameColumn(), displayName) +} + +// SetFirstName implements [domain.HumanUserRepository]. +func (u userHuman) SetFirstName(firstName string) database.Change { + return database.NewChange(u.FirstNameColumn(), firstName) +} + +// SetGender implements [domain.HumanUserRepository]. +func (u userHuman) SetGender(gender domain.HumanGender) database.Change { + return database.NewChange(u.genderColumn(), gender) +} + +// SetLastName implements [domain.HumanUserRepository]. +func (u userHuman) SetLastName(lastName string) database.Change { + return database.NewChange(u.LastNameColumn(), lastName) +} + +// SkipMultifactorInitializationAt implements [domain.HumanUserRepository]. +func (u userHuman) SkipMultifactorInitializationAt(skippedAt time.Time) database.Change { + if skippedAt.IsZero() { + return u.SkipMultifactorInitialization() + } + return database.NewChange(u.multifactorInitializationSkippedAtColumn(), skippedAt) +} + +// SkipMultifactorInitialization implements [domain.HumanUserRepository]. +func (u userHuman) SkipMultifactorInitialization() database.Change { + return database.NewChange(u.multifactorInitializationSkippedAtColumn(), database.NowInstruction) +} + +// SetNickname implements [domain.HumanUserRepository]. +func (u userHuman) SetNickname(nickname string) database.Change { + return database.NewChange(u.NicknameColumn(), nickname) +} + +// SetPreferredLanguage implements [domain.HumanUserRepository]. +func (u userHuman) SetPreferredLanguage(preferredLanguage language.Tag) database.Change { + if preferredLanguage == language.Und { + return database.NewChangeToNull(u.preferredLanguageColumn()) + } + return database.NewChange(u.preferredLanguageColumn(), preferredLanguage.String()) +} + +// SetVerification implements [domain.HumanUserRepository]. +func (u userHuman) SetVerification(verification domain.VerificationType) database.Change { + switch typ := verification.(type) { + case *domain.VerificationTypeInit: + return database.NewCTEChange(func(builder *database.StatementBuilder) { + var ( + createdAt any = database.NowInstruction + expiry any = database.NullInstruction + id any = database.DefaultInstruction + ) + if !typ.CreatedAt.IsZero() { + createdAt = typ.CreatedAt + } + if typ.Expiry != nil { + expiry = *typ.Expiry + } + if typ.ID != nil { + id = *typ.ID + } + builder.WriteString("INSERT INTO zitadel.verifications(instance_id, user_id, id, code, created_at, expiry) SELECT instance_id, id, ") + builder.WriteArgs(id, typ.Code, createdAt, expiry) + builder.WriteString(" FROM ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + builder.WriteString(" RETURNING id") + }, nil) + case *domain.VerificationTypeSucceeded: + return database.NewCTEChange(func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM zitadel.verifications USING existing_user") + + writeCondition(builder, database.And( + database.NewColumnCondition(u.verification.instanceIDColumn(), existingHumanUser.InstanceIDColumn()), + database.NewColumnCondition(u.verification.userIDColumn(), existingHumanUser.IDColumn()), + database.NewTextCondition(u.verification.idColumn(), database.TextOperationEqual, *typ.ID), + )) + }, nil) + + case *domain.VerificationTypeUpdate: + changes := make(database.Changes, 0, 3) + if typ.Code != nil { + changes = append(changes, database.NewChange(u.verification.codeColumn(), typ.Code)) + } + if typ.Expiry != nil { + changes = append(changes, database.NewChange(u.verification.expiryColumn(), *typ.Expiry)) + } + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE zitadel.verifications SET ") + err := changes.Write(builder) + logging.New(logging.StreamRuntime).Debug("write changes in cte failed", "error", err) + builder.WriteString(" FROM existing_user") + writeCondition(builder, database.And( + database.NewColumnCondition(u.verification.instanceIDColumn(), existingHumanUser.InstanceIDColumn()), + database.NewColumnCondition(u.verification.userIDColumn(), existingHumanUser.IDColumn()), + database.NewTextCondition(u.verification.idColumn(), database.TextOperationEqual, *typ.ID), + )) + }, + nil, + ) + case *domain.VerificationTypeSkipped: + panic("skip verification is not supported for verifications") + case *domain.VerificationTypeFailed: + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE ") + builder.WriteString(u.verification.qualifiedTableName()) + builder.WriteString(" SET ") + err := database.NewIncrementColumnChange(u.verification.failedAttemptsColumn(), database.Coalesce(u.verification.failedAttemptsColumn(), 0)).Write(builder) + logging.New(logging.StreamRuntime).Debug("write cte failed", "error", err) + builder.WriteString(" FROM existing_user") + writeCondition(builder, database.And( + database.NewColumnCondition(u.verification.instanceIDColumn(), existingHumanUser.InstanceIDColumn()), + database.NewColumnCondition(u.verification.userIDColumn(), existingHumanUser.IDColumn()), + database.NewTextCondition(u.verification.idColumn(), database.TextOperationEqual, *typ.ID), + )) + }, + nil, + ) + } + panic(fmt.Sprintf("undefined verification type %T", verification)) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// DisplayNameCondition implements [domain.HumanUserRepository]. +func (u userHuman) DisplayNameCondition(op database.TextOperation, displayName string) database.Condition { + return database.NewTextCondition(u.DisplayNameColumn(), op, displayName) +} + +// FirstNameCondition implements [domain.HumanUserRepository]. +func (u userHuman) FirstNameCondition(op database.TextOperation, firstName string) database.Condition { + return database.NewTextCondition(u.FirstNameColumn(), op, firstName) +} + +// LastNameCondition implements [domain.HumanUserRepository]. +func (u userHuman) LastNameCondition(op database.TextOperation, lastName string) database.Condition { + return database.NewTextCondition(u.LastNameColumn(), op, lastName) +} + +// NicknameCondition implements [domain.HumanUserRepository]. +func (u userHuman) NicknameCondition(op database.TextOperation, nickname string) database.Condition { + return database.NewTextCondition(u.NicknameColumn(), op, nickname) +} + +// PreferredLanguageCondition implements [domain.HumanUserRepository]. +func (u userHuman) PreferredLanguageCondition(lang language.Tag) database.Condition { + return database.NewTextCondition(u.preferredLanguageColumn(), database.TextOperationEqual, lang.String()) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +// DisplayNameColumn implements [domain.HumanUserRepository]. +func (u userHuman) DisplayNameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "display_name") +} + +func (u userHuman) genderColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "gender") +} + +func (u userHuman) avatarKeyColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "avatar_key") +} + +// FirstNameColumn implements [domain.HumanUserRepository]. +func (u userHuman) FirstNameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "first_name") +} + +// LastNameColumn implements [domain.HumanUserRepository]. +func (u userHuman) LastNameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "last_name") +} + +// NicknameColumn implements [domain.HumanUserRepository]. +func (u userHuman) NicknameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "nickname") +} + +func (u userHuman) multifactorInitializationSkippedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "multifactor_initialization_skipped_at") +} + +func (u userHuman) preferredLanguageColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "preferred_language") +} + +// ------------------------------------------------------------- +// scanners +// ------------------------------------------------------------- + +// ------------------------------------------------------------- +// sub repositories +// ------------------------------------------------------------- diff --git a/backend/v3/storage/database/repository/user_human_email.go b/backend/v3/storage/database/repository/user_human_email.go new file mode 100644 index 000000000..d7d8771a6 --- /dev/null +++ b/backend/v3/storage/database/repository/user_human_email.go @@ -0,0 +1,132 @@ +package repository + +import ( + "fmt" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +func (u userHuman) SetEmail(email string) database.Change { + return database.NewChange(u.EmailColumn(), email) +} + +func (u userHuman) SetUnverifiedEmail(email string) database.Change { + return database.NewChange(u.unverifiedEmailColumn(), email) +} + +// SetEmailVerification implements [domain.HumanUserRepository]. +func (u userHuman) SetEmailVerification(verification domain.VerificationType) database.Change { + switch typ := verification.(type) { + case *domain.VerificationTypeInit: + return u.verification.init(typ, existingHumanUser.unqualifiedTableName(), existingHumanUser.emailVerificationIDColumn()) + case *domain.VerificationTypeUpdate: + return u.verification.update(typ, existingHumanUser.unqualifiedTableName(), + existingHumanUser.InstanceIDColumn(), existingHumanUser.emailVerificationIDColumn()) + case *domain.VerificationTypeSucceeded: + return database.NewChanges( + u.verification.verified(typ, existingUser.unqualifiedTableName(), existingUser.InstanceIDColumn(), + existingHumanUser.emailVerificationIDColumn(), u.emailVerifiedAtColumn(), u.failedEmailOTPAttemptsColumn(), + ), + database.NewChangeToColumn(u.EmailColumn(), u.unverifiedEmailColumn()), + ) + case *domain.VerificationTypeFailed: + return u.verification.failed(existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), existingHumanUser.emailVerificationIDColumn()) + case *domain.VerificationTypeSkipped: + return u.verification.skipped(typ, u.emailVerifiedAtColumn(), u.emailVerificationIDColumn(), u.failedEmailOTPAttemptsColumn()) + } + panic(fmt.Sprintf("type not allowed for email verification change %T", verification)) +} + +// EnableEmailOTPAt implements [domain.HumanUserRepository]. +func (u userHuman) EnableEmailOTPAt(enabledAt time.Time) database.Change { + return database.NewChange(u.emailOTPEnabledAtColumn(), enabledAt) +} + +// EnableEmailOTP implements [domain.HumanUserRepository]. +func (u userHuman) EnableEmailOTP() database.Change { + return database.NewChange(u.emailOTPEnabledAtColumn(), database.NowInstruction) +} + +// DisableEmailOTP implements [domain.HumanUserRepository]. +func (u userHuman) DisableEmailOTP() database.Change { + return database.NewChangeToNull(u.emailOTPEnabledAtColumn()) +} + +func (u userHuman) SetLastSuccessfulEmailOTPCheck(checkedAt time.Time) database.Change { + if checkedAt.IsZero() { + return database.NewChanges( + database.NewChange(u.lastSuccessfulEmailOTPCheckColumn(), database.NowInstruction), + u.ResetEmailOTPFailedAttempts(), + ) + } + return database.NewChanges( + database.NewChange(u.lastSuccessfulEmailOTPCheckColumn(), checkedAt), + u.ResetEmailOTPFailedAttempts(), + ) +} + +func (u userHuman) IncrementEmailOTPFailedAttempts() database.Change { + return database.NewIncrementColumnChange(u.failedEmailOTPAttemptsColumn(), database.Coalesce(u.failedEmailOTPAttemptsColumn(), 0)) +} + +func (u userHuman) ResetEmailOTPFailedAttempts() database.Change { + return database.NewChange(u.failedEmailOTPAttemptsColumn(), 0) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// EmailCondition implements [domain.HumanUserRepository]. +func (u userHuman) EmailCondition(op database.TextOperation, email string) database.Condition { + return database.Or(u.VerifiedEmailCondition(op, email), u.UnverifiedEmailCondition(op, email)) +} + +// UnverifiedEmailCondition implements [domain.HumanUserRepository]. +func (u userHuman) UnverifiedEmailCondition(op database.TextOperation, email string) database.Condition { + return database.NewTextCondition(u.unverifiedEmailColumn(), op, email) +} + +// VerifiedEmailCondition implements [domain.HumanUserRepository]. +func (u userHuman) VerifiedEmailCondition(op database.TextOperation, email string) database.Condition { + return database.NewTextCondition(u.EmailColumn(), op, email) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +// EmailColumn implements [domain.HumanUserRepository]. +func (u userHuman) EmailColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email") +} + +func (u userHuman) unverifiedEmailColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "unverified_email") +} + +func (u userHuman) emailOTPEnabledAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email_otp_enabled_at") +} + +func (u userHuman) emailVerifiedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email_verified_at") +} + +func (u userHuman) emailVerificationIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email_verification_id") +} + +func (u userHuman) failedEmailOTPAttemptsColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email_otp_failed_attempts") +} + +func (u userHuman) lastSuccessfulEmailOTPCheckColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "email_otp_last_successful_check") +} diff --git a/backend/v3/storage/database/repository/user_human_password.go b/backend/v3/storage/database/repository/user_human_password.go new file mode 100644 index 000000000..7980d50e3 --- /dev/null +++ b/backend/v3/storage/database/repository/user_human_password.go @@ -0,0 +1,92 @@ +package repository + +import ( + "fmt" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +func (u userHuman) SetPassword(password string) database.Change { + return database.NewChange(u.passwordHashColumn(), password) +} + +// SetResetPasswordVerification implements [domain.HumanUserRepository]. +func (u userHuman) SetResetPasswordVerification(verification domain.VerificationType) database.Change { + switch typ := verification.(type) { + case *domain.VerificationTypeInit: + return u.verification.init(typ, existingUser.unqualifiedTableName(), existingHumanUser.passwordVerificationIDColumn()) + case *domain.VerificationTypeUpdate: + return u.verification.update(typ, existingHumanUser.unqualifiedTableName(), + existingHumanUser.InstanceIDColumn(), existingHumanUser.passwordVerificationIDColumn(), + ) + case *domain.VerificationTypeSucceeded: + return u.verification.verified(typ, existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), + existingHumanUser.passwordVerificationIDColumn(), u.passwordVerifiedAtColumn(), u.failedPasswordAttemptsColumn(), + ) + case *domain.VerificationTypeSkipped: + return u.verification.skipped(typ, u.passwordVerifiedAtColumn(), u.passwordVerificationIDColumn(), u.failedPasswordAttemptsColumn()) + case *domain.VerificationTypeFailed: + return u.verification.failed(existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), existingHumanUser.passwordVerificationIDColumn()) + } + panic(fmt.Sprintf("undefined verification type %T", verification)) +} + +// SetPasswordChangeRequired implements [domain.HumanUserRepository]. +func (u userHuman) SetPasswordChangeRequired(required bool) database.Change { + return database.NewChange(u.passwordChangeRequiredColumn(), required) +} + +func (u userHuman) SetLastSuccessfulPasswordCheck(checkedAt time.Time) database.Change { + if checkedAt.IsZero() { + return database.NewChanges( + database.NewChange(u.lastSuccessfulPasswordCheckColumn(), database.NowInstruction), + u.ResetPasswordFailedAttempts(), + ) + } + return database.NewChanges( + database.NewChange(u.lastSuccessfulPasswordCheckColumn(), checkedAt), + u.ResetPasswordFailedAttempts(), + ) +} + +func (u userHuman) IncrementPasswordFailedAttempts() database.Change { + return database.NewIncrementColumnChange(u.failedPasswordAttemptsColumn(), database.Coalesce(u.failedPasswordAttemptsColumn(), 0)) +} + +func (u userHuman) ResetPasswordFailedAttempts() database.Change { + return database.NewChange(u.failedPasswordAttemptsColumn(), 0) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userHuman) passwordHashColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_hash") +} + +func (u userHuman) passwordChangeRequiredColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_change_required") +} + +func (u userHuman) passwordVerifiedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_changed_at") +} + +func (u userHuman) passwordVerificationIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_verification_id") +} + +func (u userHuman) lastSuccessfulPasswordCheckColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_last_successful_check") +} + +func (u userHuman) failedPasswordAttemptsColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "password_failed_attempts") +} diff --git a/backend/v3/storage/database/repository/user_human_phone.go b/backend/v3/storage/database/repository/user_human_phone.go new file mode 100644 index 000000000..d7c84c72a --- /dev/null +++ b/backend/v3/storage/database/repository/user_human_phone.go @@ -0,0 +1,148 @@ +package repository + +import ( + "fmt" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetPhone implements [domain.HumanUserRepository]. +func (u userHuman) SetPhone(phone string) database.Change { + return database.NewChange(u.phoneColumn(), phone) +} + +// SetUnverifiedPhone implements [domain.HumanUserRepository]. +func (u userHuman) SetUnverifiedPhone(phone string) database.Change { + return database.NewChange(u.unverifiedPhoneColumn(), phone) +} + +// SetPhoneVerification implements [domain.HumanUserRepository]. +func (u userHuman) SetPhoneVerification(verification domain.VerificationType) database.Change { + switch typ := verification.(type) { + case *domain.VerificationTypeInit: + return u.verification.init(typ, existingHumanUser.unqualifiedTableName(), existingHumanUser.phoneVerificationIDColumn()) + case *domain.VerificationTypeUpdate: + return u.verification.update(typ, existingHumanUser.unqualifiedTableName(), + existingHumanUser.InstanceIDColumn(), existingHumanUser.phoneVerificationIDColumn(), + ) + case *domain.VerificationTypeSucceeded: + return database.NewChanges( + u.verification.verified(typ, existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), + existingHumanUser.phoneVerificationIDColumn(), u.phoneVerifiedAtColumn(), u.failedSMSOTPAttemptsColumn(), + ), + database.NewChangeToColumn(u.phoneColumn(), u.unverifiedPhoneColumn()), + ) + case *domain.VerificationTypeFailed: + return u.verification.failed(existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), existingHumanUser.phoneVerificationIDColumn()) + case *domain.VerificationTypeSkipped: + return u.verification.skipped(typ, u.phoneVerifiedAtColumn(), u.phoneVerificationIDColumn(), u.failedSMSOTPAttemptsColumn()) + } + panic(fmt.Sprintf("type not allowed for phone verification change %T", verification)) +} + +// RemovePhone implements [domain.HumanUserRepository]. +func (u userHuman) RemovePhone() database.Change { + return database.NewChanges( + database.NewChangeToNull(u.phoneColumn()), + database.NewChangeToNull(u.phoneVerifiedAtColumn()), + database.NewChangeToNull(u.phoneVerificationIDColumn()), + database.NewChangeToNull(u.smsOTPEnabledAtColumn()), + database.NewChangeToNull(u.lastSuccessfulSMSOTPCheckColumn()), + database.NewChangeToNull(u.failedSMSOTPAttemptsColumn()), + ) +} + +// EnableSMSOTPAt implements [domain.HumanUserRepository]. +func (u userHuman) EnableSMSOTPAt(enabledAt time.Time) database.Change { + return database.NewChange(u.smsOTPEnabledAtColumn(), enabledAt) +} + +// EnableSMSOTP implements [domain.HumanUserRepository]. +func (u userHuman) EnableSMSOTP() database.Change { + return database.NewChange(u.smsOTPEnabledAtColumn(), database.NowInstruction) +} + +// DisableSMSOTP implements [domain.HumanUserRepository]. +func (u userHuman) DisableSMSOTP() database.Change { + return database.NewChangeToNull(u.smsOTPEnabledAtColumn()) +} + +func (u userHuman) SetLastSuccessfulSMSOTPCheck(checkedAt time.Time) database.Change { + if checkedAt.IsZero() { + return database.NewChanges( + database.NewChange(u.lastSuccessfulSMSOTPCheckColumn(), database.NowInstruction), + u.ResetSMSOTPFailedAttempts(), + ) + } + return database.NewChanges( + database.NewChange(u.lastSuccessfulSMSOTPCheckColumn(), checkedAt), + u.ResetSMSOTPFailedAttempts(), + ) +} + +// IncrementSMSOTPFailedAttempts implements [domain.HumanUserRepository]. +func (u userHuman) IncrementSMSOTPFailedAttempts() database.Change { + return database.NewIncrementColumnChange(u.failedSMSOTPAttemptsColumn(), database.Coalesce(u.failedSMSOTPAttemptsColumn(), 0)) +} + +// ResetSMSOTPFailedAttempts implements [domain.HumanUserRepository]. +func (u userHuman) ResetSMSOTPFailedAttempts() database.Change { + return database.NewChange(u.failedSMSOTPAttemptsColumn(), 0) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// PhoneCondition implements [domain.HumanUserRepository]. +func (u userHuman) PhoneCondition(op database.TextOperation, phone string) database.Condition { + return database.Or(u.UnverifiedPhoneCondition(op, phone), u.VerifiedPhoneCondition(op, phone)) +} + +// UnverifiedPhoneCondition implements [domain.HumanUserRepository]. +func (u userHuman) UnverifiedPhoneCondition(op database.TextOperation, phone string) database.Condition { + return database.NewTextCondition(u.unverifiedPhoneColumn(), op, phone) +} + +// VerifiedPhoneCondition implements [domain.HumanUserRepository]. +func (u userHuman) VerifiedPhoneCondition(op database.TextOperation, phone string) database.Condition { + return database.NewTextCondition(u.phoneColumn(), op, phone) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userHuman) phoneColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "phone") +} + +func (u userHuman) unverifiedPhoneColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "unverified_phone") +} + +func (u userHuman) phoneVerifiedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "phone_verified_at") +} + +func (u userHuman) phoneVerificationIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "phone_verification_id") +} + +func (u userHuman) smsOTPEnabledAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "sms_otp_enabled_at") +} + +func (u userHuman) lastSuccessfulSMSOTPCheckColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "sms_otp_last_successful_check") +} + +func (u userHuman) failedSMSOTPAttemptsColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "sms_otp_failed_attempts") +} diff --git a/backend/v3/storage/database/repository/user_human_test.go b/backend/v3/storage/database/repository/user_human_test.go new file mode 100644 index 000000000..891e6ca02 --- /dev/null +++ b/backend/v3/storage/database/repository/user_human_test.go @@ -0,0 +1,598 @@ +package repository_test + +import ( + "context" + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/crypto" +) + +func Test_humanUser_create(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + // tx := pool + + userRepo := repository.UserRepository() + + instanceID := createInstance(t, tx) + orgID := createOrganization(t, tx, instanceID) + createdAt := time.Now().Round(time.Millisecond).UTC() + + existingUserID := createMachineUser(t, tx, instanceID, orgID) + password := gofakeit.Password(true, true, true, true, false, 16) + + type args struct { + user *domain.User + } + type want struct { + err error + user *domain.User + } + type test struct { + name string + args args + want want + } + tests := []test{ + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + + return test{ + name: "minimal representation", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + UnverifiedAddress: email, + }, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + UnverifiedAddress: email, + }, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + + return test{ + name: "including optional profile fields", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Nickname: "Nick", + DisplayName: firstName + " " + lastName, + PreferredLanguage: language.Georgian, + Gender: domain.HumanGenderFemale, + AvatarKey: "http://localhost:8080/my/profile/picture", + MultifactorInitializationSkippedAt: createdAt, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + Address: email, + }, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Nickname: "Nick", + DisplayName: firstName + " " + lastName, + PreferredLanguage: language.Georgian, + Gender: domain.HumanGenderFemale, + AvatarKey: "http://localhost:8080/my/profile/picture", + MultifactorInitializationSkippedAt: createdAt, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + code := &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes256", + KeyID: "key-id", + Crypted: []byte("crypted"), + } + + return test{ + name: "with unverified email without expiry", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + UnverifiedAddress: email, + PendingVerification: &domain.Verification{ + ID: "test", + Code: code, + }, + }, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + UnverifiedAddress: email, + PendingVerification: &domain.Verification{ + ID: "test", + Code: code, + CreatedAt: createdAt, + UpdatedAt: createdAt, + }, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + phone := gofakeit.Phone() + code := &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes256", + KeyID: "key-id", + Crypted: []byte("crypted"), + } + + return test{ + name: "with unverified phone without expiry", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Phone: &domain.HumanPhone{ + UnverifiedNumber: phone, + PendingVerification: &domain.Verification{ + ID: "id", + Code: code, + }, + }, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + Phone: &domain.HumanPhone{ + UnverifiedNumber: phone, + PendingVerification: &domain.Verification{ + CreatedAt: createdAt, + UpdatedAt: createdAt, + ID: "id", + Code: code, + }, + }, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + passkeys := []*domain.Passkey{ + { + ID: gofakeit.UUID(), + KeyID: []byte(gofakeit.UUID()), + Type: domain.PasskeyTypePasswordless, + Name: "passwordless", + PublicKey: []byte("public key"), + AttestationType: "don't know", + AuthenticatorAttestationGUID: []byte("aaguid"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + Challenge: []byte("challenge"), + VerifiedAt: createdAt, + RelyingPartyID: "rpid", + }, + { + ID: gofakeit.UUID(), + KeyID: []byte(gofakeit.UUID()), + Type: domain.PasskeyTypeU2F, + Name: "u2f", + PublicKey: []byte("public key"), + AttestationType: "don't know", + AuthenticatorAttestationGUID: []byte("aaguid"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + Challenge: []byte("challenge"), + VerifiedAt: createdAt, + RelyingPartyID: "rpid", + }, + } + + return test{ + name: "with verified passkeys", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + Address: email, + }, + Passkeys: passkeys, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + Passkeys: passkeys, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + code := &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes256", + KeyID: "key-id", + Crypted: []byte("crypted"), + } + verifications := []*domain.Verification{ + { + ID: gofakeit.UUID(), + Code: code, + }, + { + ID: gofakeit.UUID(), + Code: code, + }, + } + + return test{ + name: "with verifications", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + Address: email, + }, + Verifications: verifications, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + Verifications: verifications, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + firstName := gofakeit.FirstName() + lastName := gofakeit.LastName() + email := gofakeit.Email() + + return test{ + name: "with totp", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + }, + Email: domain.HumanEmail{ + Address: email, + }, + TOTP: &domain.HumanTOTP{ + VerifiedAt: createdAt, + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + }, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Human: &domain.HumanUser{ + FirstName: firstName, + LastName: lastName, + Email: domain.HumanEmail{ + Address: email, + UnverifiedAddress: email, + VerifiedAt: createdAt, + }, + Password: domain.HumanPassword{ + Hash: password, + IsChangeRequired: true, + ChangedAt: createdAt, + }, + TOTP: &domain.HumanTOTP{ + VerifiedAt: createdAt, + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + }, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + return test{ + name: "already exists", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: existingUserID, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + }, + }, + }, + want: want{ + err: new(database.UniqueError), + }, + } + }(), + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + + err = userRepo.Create(t.Context(), savepoint, tt.args.user) + require.ErrorIs(t, err, tt.want.err) + if tt.want.err != nil { + return + } + got, err := userRepo.Get(t.Context(), savepoint, database.WithCondition( + userRepo.PrimaryKeyCondition(tt.want.user.InstanceID, tt.want.user.ID), + )) + require.NoError(t, err) + assertUser(t, tt.want.user, got) + }) + } +} diff --git a/backend/v3/storage/database/repository/user_human_totp.go b/backend/v3/storage/database/repository/user_human_totp.go new file mode 100644 index 000000000..906da8347 --- /dev/null +++ b/backend/v3/storage/database/repository/user_human_totp.go @@ -0,0 +1,82 @@ +package repository + +import ( + "time" + + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/internal/crypto" +) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetTOTPSecret implements [domain.HumanUserRepository]. +func (u userHuman) SetTOTPSecret(secret *crypto.CryptoValue) database.Change { + return database.NewChange(u.totpSecretColumn(), secret) +} + +// SetTOTPVerifiedAt implements [domain.HumanUserRepository]. +func (u userHuman) SetTOTPVerifiedAt(verifiedAt time.Time) database.Change { + if verifiedAt.IsZero() { + return database.NewChange(u.totpVerifiedAtColumn(), database.NowInstruction) + } + return database.NewChange(u.totpVerifiedAtColumn(), verifiedAt) +} + +// RemoveTOTP implements [domain.HumanUserRepository]. +func (u userHuman) RemoveTOTP() database.Change { + return database.NewChanges( + database.NewChangeToNull(u.totpSecretColumn()), + database.NewChangeToNull(u.totpVerifiedAtColumn()), + database.NewChangeToNull(u.lastSuccessfulTOTPCheckColumn()), + ) +} + +// SetLastSuccessfulTOTPCheck implements [domain.HumanUserRepository]. +func (u userHuman) SetLastSuccessfulTOTPCheck(checkedAt time.Time) database.Change { + if checkedAt.IsZero() { + return database.NewChanges( + database.NewChange(u.lastSuccessfulTOTPCheckColumn(), database.NowInstruction), + u.ResetTOTPFailedAttempts(), + ) + } + return database.NewChanges( + database.NewChange(u.lastSuccessfulTOTPCheckColumn(), checkedAt), + u.ResetTOTPFailedAttempts(), + ) +} + +// IncrementTOTPFailedAttempts implements [domain.HumanUserRepository]. +func (u userHuman) IncrementTOTPFailedAttempts() database.Change { + return database.NewIncrementColumnChange(u.totpFailedAttemptsColumn(), database.Coalesce(u.totpFailedAttemptsColumn(), 0)) +} + +// ResetTOTPFailedAttempts implements [domain.HumanUserRepository]. +func (u userHuman) ResetTOTPFailedAttempts() database.Change { + return database.NewChange(u.totpFailedAttemptsColumn(), 0) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userHuman) totpSecretColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "totp_secret") +} + +func (u userHuman) totpVerifiedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "totp_verified_at") +} + +func (u userHuman) lastSuccessfulTOTPCheckColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "totp_last_successful_check") +} + +func (u userHuman) totpFailedAttemptsColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "totp_failed_attempts") +} diff --git a/backend/v3/storage/database/repository/user_idp_links.go b/backend/v3/storage/database/repository/user_idp_links.go new file mode 100644 index 000000000..93b2ee4dc --- /dev/null +++ b/backend/v3/storage/database/repository/user_idp_links.go @@ -0,0 +1,161 @@ +package repository + +import ( + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/instrumentation/logging" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type userIdentityProviderLink struct{} + +func (u userIdentityProviderLink) unqualifiedTableName() string { + return "user_identity_provider_links" +} + +func (u userIdentityProviderLink) qualifiedTableName() string { + return "zitadel.user_identity_provider_links" +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// AddIdentityProviderLink implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) AddIdentityProviderLink(link *domain.IdentityProviderLink) database.Change { + var createdAt, updatedAt any = database.NowInstruction, database.NowInstruction + if !link.CreatedAt.IsZero() { + createdAt = link.CreatedAt + } + if !link.UpdatedAt.IsZero() { + updatedAt = link.UpdatedAt + } + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("INSERT INTO ") + builder.WriteString(u.qualifiedTableName()) + builder.WriteString("(instance_id, user_id, provided_user_id, provided_username, identity_provider_id, created_at, updated_at) SELECT ") + database.Columns{ + existingHumanUser.InstanceIDColumn(), + existingHumanUser.IDColumn(), + }.WriteQualified(builder) + builder.WriteString(", ") + builder.WriteArgs( + link.ProvidedUserID, + link.ProvidedUsername, + link.ProviderID, + createdAt, + updatedAt, + ) + builder.WriteString(" FROM ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + builder.WriteString(" RETURNING *") + }, nil, + ) +} + +// UpdateIdentityProviderLink implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) UpdateIdentityProviderLink(condition database.Condition, changes ...database.Change) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE zitadel.user_identity_provider_links SET ") + err := database.Changes(changes).Write(builder) + logging.New(logging.StreamRuntime).Debug("write changes in cte failed", "error", err) + builder.WriteString(" FROM ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + writeCondition(builder, database.And( + database.NewColumnCondition(existingHumanUser.InstanceIDColumn(), u.instanceIDColumn()), + database.NewColumnCondition(existingHumanUser.IDColumn(), u.userIDColumn()), + condition, + )) + }, nil, + ) +} + +// RemoveIdentityProviderLink implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) RemoveIdentityProviderLink(providerID string, providedUserID string) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM ") + builder.WriteString(u.qualifiedTableName()) + builder.WriteString(" USING ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + writeCondition(builder, database.And( + database.NewColumnCondition( + existingHumanUser.InstanceIDColumn(), + u.instanceIDColumn(), + ), + database.NewColumnCondition( + existingHumanUser.IDColumn(), + u.userIDColumn(), + ), + database.NewTextCondition( + u.providerIDColumn(), + database.TextOperationEqual, + providerID, + ), + database.NewTextCondition( + u.providedUserIDColumn(), + database.TextOperationEqual, + providedUserID, + ), + )) + }, nil, + ) +} + +// SetIdentityProviderLinkUsername implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) SetIdentityProviderLinkUsername(username string) database.Change { + return database.NewChange(u.providedUsernameColumn(), username) +} + +// SetIdentityProviderLinkProvidedID implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) SetIdentityProviderLinkProvidedID(providedUserID string) database.Change { + return database.NewChange(u.providedUserIDColumn(), providedUserID) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// IdentityProviderLinkConditions implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) IdentityProviderLinkConditions() domain.HumanIdentityProviderLinkConditions { + return u +} + +// ProviderIDCondition implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) ProviderIDCondition(idpID string) database.Condition { + return database.NewTextCondition(u.providerIDColumn(), database.TextOperationEqual, idpID) +} + +// ProvidedUserIDCondition implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) ProvidedUserIDCondition(providedUserID string) database.Condition { + return database.NewTextCondition(u.providedUserIDColumn(), database.TextOperationEqual, providedUserID) +} + +// ProvidedUsernameCondition implements [domain.HumanUserRepository]. +func (u userIdentityProviderLink) ProvidedUsernameCondition(op database.TextOperation, username string) database.Condition { + return database.NewTextCondition(u.providedUsernameColumn(), op, username) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userIdentityProviderLink) instanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} +func (u userIdentityProviderLink) userIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "user_id") +} + +func (u userIdentityProviderLink) providerIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "identity_provider_id") +} + +func (u userIdentityProviderLink) providedUserIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "provided_user_id") +} + +func (u userIdentityProviderLink) providedUsernameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "provided_username") +} diff --git a/backend/v3/storage/database/repository/user_invite.go b/backend/v3/storage/database/repository/user_invite.go new file mode 100644 index 000000000..1131c4429 --- /dev/null +++ b/backend/v3/storage/database/repository/user_invite.go @@ -0,0 +1,49 @@ +package repository + +import ( + "fmt" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetInviteVerification implements [domain.HumanUserRepository]. +func (u userHuman) SetInviteVerification(verification domain.VerificationType) database.Change { + switch typ := verification.(type) { + case *domain.VerificationTypeInit: + return u.verification.init(typ, existingUser.unqualifiedTableName(), existingHumanUser.inviteVerificationIDColumn()) + case *domain.VerificationTypeUpdate: + return u.verification.update(typ, existingHumanUser.unqualifiedTableName(), + existingHumanUser.InstanceIDColumn(), existingHumanUser.inviteVerificationIDColumn(), + ) + case *domain.VerificationTypeSucceeded: + return u.verification.verified(typ, existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), + existingHumanUser.inviteVerificationIDColumn(), u.inviteAcceptedAtColumn(), u.failedInviteAttemptsColumn(), + ) + case *domain.VerificationTypeSkipped: + return u.verification.skipped(typ, u.inviteAcceptedAtColumn(), u.inviteVerificationIDColumn(), u.failedInviteAttemptsColumn()) + case *domain.VerificationTypeFailed: + return u.verification.failed(existingHumanUser.unqualifiedTableName(), existingHumanUser.InstanceIDColumn(), existingHumanUser.inviteVerificationIDColumn()) + } + panic(fmt.Sprintf("undefined verification type %T", verification)) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userHuman) inviteVerificationIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "invite_verification_id") +} + +func (u userHuman) inviteAcceptedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "invite_accepted_at") +} + +func (u userHuman) failedInviteAttemptsColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "invite_failed_attempts") +} diff --git a/backend/v3/storage/database/repository/user_machine.go b/backend/v3/storage/database/repository/user_machine.go new file mode 100644 index 000000000..8ff689ad9 --- /dev/null +++ b/backend/v3/storage/database/repository/user_machine.go @@ -0,0 +1,103 @@ +package repository + +import ( + "context" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type userMachine struct { + user + machineKey + userPersonalAccessToken +} + +func (u userMachine) create(ctx context.Context, builder *database.StatementBuilder, client database.QueryExecutor, user *domain.User) error { + changes := make(database.Changes, 0, len(user.Machine.Keys)+len(user.Machine.PATs)) + for _, key := range user.Machine.Keys { + changes = append(changes, u.AddKey(key)) + } + for _, pat := range user.Machine.PATs { + changes = append(changes, u.AddPersonalAccessToken(pat)) + } + for _, metadata := range user.Metadata { + changes = append(changes, u.SetMetadata(metadata)) + } + for i, change := range changes { + sessionCTE(change, i, 0, builder) + } + + builder.WriteString("INSERT INTO zitadel.users (" + + "instance_id, organization_id, id, username, state, type" + + ", name, description, secret, access_token_type, created_at, updated_at) VALUES (", + ) + + var createdAt any = database.NowInstruction + if !user.CreatedAt.IsZero() { + createdAt = user.CreatedAt + } + builder.WriteArgs( + user.InstanceID, user.OrganizationID, user.ID, user.Username, user.State, "machine", + user.Machine.Name, user.Machine.Description, user.Machine.Secret, uint8(user.Machine.AccessTokenType), + createdAt, createdAt, + ) + + builder.WriteString(") RETURNING created_at, updated_at") + + return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&user.CreatedAt, &user.UpdatedAt) +} + +// Update implements [domain.MachineUserRepository]. +func (u userMachine) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) { + if !condition.IsRestrictingColumn(u.TypeColumn()) { + condition = database.And(condition, u.TypeCondition(domain.UserTypeMachine)) + } + return u.user.Update(ctx, client, condition, changes...) +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetAccessTokenType implements [domain.MachineUserRepository]. +func (u userMachine) SetAccessTokenType(tokenType domain.AccessTokenType) database.Change { + return database.NewChange(u.accessTokenTypeColumn(), tokenType) +} + +// SetDescription implements [domain.MachineUserRepository]. +func (u userMachine) SetDescription(description string) database.Change { + return database.NewChange(u.descriptionColumn(), description) +} + +// SetName implements [domain.MachineUserRepository]. +func (u userMachine) SetName(name string) database.Change { + return database.NewChange(u.nameColumn(), name) +} + +// SetSecret implements [domain.MachineUserRepository]. +func (u userMachine) SetSecret(secret *string) database.Change { + return database.NewChangePtr(u.secretColumn(), secret) +} + +var _ domain.MachineUserRepository = (*userMachine)(nil) + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userMachine) nameColumn() database.Column { + return database.NewColumn(u.user.unqualifiedTableName(), "name") +} + +func (u userMachine) descriptionColumn() database.Column { + return database.NewColumn(u.user.unqualifiedTableName(), "description") +} + +func (u userMachine) accessTokenTypeColumn() database.Column { + return database.NewColumn(u.user.unqualifiedTableName(), "access_token_type") +} + +func (u userMachine) secretColumn() database.Column { + return database.NewColumn(u.user.unqualifiedTableName(), "secret") +} diff --git a/backend/v3/storage/database/repository/user_machine_key.go b/backend/v3/storage/database/repository/user_machine_key.go new file mode 100644 index 000000000..0126bbf08 --- /dev/null +++ b/backend/v3/storage/database/repository/user_machine_key.go @@ -0,0 +1,64 @@ +package repository + +import ( + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type machineKey struct{} + +// AddKey implements [domain.MachineUserRepository]. +func (u machineKey) AddKey(key *domain.MachineKey) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("INSERT INTO zitadel.machine_keys (" + + "instance_id, user_id, id, created_at, expires_at, type, public_key" + + ") SELECT instance_id, id, ", + ) + var createdAt any = database.NowInstruction + if !key.CreatedAt.IsZero() { + createdAt = key.CreatedAt + } + var expiresAt any = database.NullInstruction + if !key.ExpiresAt.IsZero() { + expiresAt = key.ExpiresAt + } + builder.WriteArgs(key.ID, createdAt, expiresAt, key.Type, key.PublicKey) + builder.WriteString(" FROM existing_user") + }, + nil, + ) +} + +// RemoveKey implements [domain.MachineUserRepository]. +func (u machineKey) RemoveKey(id string) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM zitadel.machine_keys WHERE " + + "(instance_id, user_id, id) = (SELECT instance_id, id, ") + builder.WriteArg(id) + builder.WriteString(" FROM existing_user)") + }, + nil, + ) +} + +func (u machineKey) qualifiedTableName() string { + return "zitadel.machine_keys" +} + +func (u machineKey) unqualifiedTableName() string { + return "machine_keys" +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u machineKey) instanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} + +func (u machineKey) userIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "user_id") +} diff --git a/backend/v3/storage/database/repository/user_machine_test.go b/backend/v3/storage/database/repository/user_machine_test.go new file mode 100644 index 000000000..439218133 --- /dev/null +++ b/backend/v3/storage/database/repository/user_machine_test.go @@ -0,0 +1,394 @@ +package repository_test + +import ( + "context" + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" +) + +func Test_machineUser_create(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + + instanceID := createInstance(t, tx) + orgID := createOrganization(t, tx, instanceID) + createdAt := time.Now().Round(time.Second) + + existingUserID := createMachineUser(t, tx, instanceID, orgID) + + type args struct { + user *domain.User + } + type want struct { + err error + user *domain.User + } + type test struct { + name string + args args + want want + } + tests := []test{ + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + return test{ + name: "minimal representation", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + return test{ + name: "with secret", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + Secret: "secret", + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + Secret: "secret", + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + return test{ + name: "with access token type", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + AccessTokenType: domain.AccessTokenTypeBearer, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + AccessTokenType: domain.AccessTokenTypeBearer, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + keys := []*domain.MachineKey{ + { + ID: gofakeit.UUID(), + PublicKey: []byte("public key"), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + Type: domain.MachineKeyTypeJSON, + }, + { + ID: gofakeit.UUID(), + PublicKey: []byte("public key"), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + Type: domain.MachineKeyTypeJSON, + }, + } + return test{ + name: "with keys", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + Keys: keys, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + Keys: keys, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + pats := []*domain.PersonalAccessToken{ + { + ID: gofakeit.UUID(), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + }, + { + ID: gofakeit.UUID(), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + Scopes: []string{"some", "scopes"}, + }, + } + return test{ + name: "with keys", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + PATs: pats, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + PATs: pats, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + id := gofakeit.UUID() + pats := []*domain.PersonalAccessToken{ + { + ID: gofakeit.UUID(), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + }, + } + keys := []*domain.MachineKey{ + { + ID: gofakeit.UUID(), + PublicKey: []byte("public key"), + CreatedAt: createdAt, + ExpiresAt: createdAt.Add(24 * time.Hour), + Type: domain.MachineKeyTypeJSON, + }, + } + return test{ + name: "all fields set", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Metadata: []*domain.Metadata{ + { + Key: "meta1", + Value: []byte("value1"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + }, + { + Key: "meta2", + Value: []byte("123"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + }, + }, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + PATs: pats, + Keys: keys, + Secret: "secret", + AccessTokenType: domain.AccessTokenTypeJWT, + }, + }, + }, + want: want{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: id, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + UpdatedAt: createdAt, + Metadata: []*domain.Metadata{ + { + InstanceID: instanceID, + Key: "meta1", + Value: []byte("value1"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + }, + { + InstanceID: instanceID, + Key: "meta2", + Value: []byte("123"), + CreatedAt: createdAt, + UpdatedAt: createdAt, + }, + }, + Machine: &domain.MachineUser{ + Name: "machine", + Description: "description", + PATs: pats, + Keys: keys, + Secret: "secret", + AccessTokenType: domain.AccessTokenTypeJWT, + }, + }, + }, + } + }(), + func() test { + username := gofakeit.Username() + return test{ + name: "already exists", + args: args{ + user: &domain.User{ + InstanceID: instanceID, + OrganizationID: orgID, + ID: existingUserID, + Username: username, + State: domain.UserStateActive, + CreatedAt: createdAt, + Machine: &domain.MachineUser{ + Name: "machine", + }, + }, + }, + want: want{ + err: new(database.UniqueError), + }, + } + }(), + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + + err = userRepo.Create(t.Context(), savepoint, tt.args.user) + require.ErrorIs(t, err, tt.want.err) + if tt.want.err != nil { + return + } + got, err := userRepo.Get(t.Context(), savepoint, database.WithCondition( + userRepo.PrimaryKeyCondition(tt.want.user.InstanceID, tt.want.user.ID), + )) + require.NoError(t, err) + assertUser(t, tt.want.user, got) + }) + } +} diff --git a/backend/v3/storage/database/repository/user_metadata.go b/backend/v3/storage/database/repository/user_metadata.go new file mode 100644 index 000000000..2c6dea94f --- /dev/null +++ b/backend/v3/storage/database/repository/user_metadata.go @@ -0,0 +1,121 @@ +package repository + +import ( + "strings" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type userMetadata struct{} + +func (userMetadata) qualifiedTableName() string { + return "zitadel.user_metadata" +} + +func (userMetadata) unqualifiedTableName() string { + return "user_metadata" +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// SetMetadata implements [domain.UserRepository]. +func (u userMetadata) SetMetadata(metadata ...*domain.Metadata) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("INSERT INTO zitadel.user_metadata(instance_id, user_id, key, value, created_at, updated_at)") + builder.WriteString(" SELECT existing_user.instance_id, existing_user.id, md.key::TEXT, md.value::BYTEA, md.created_at::TIMESTAMPTZ, md.updated_at::TIMESTAMPTZ") + builder.WriteString(" FROM existing_user CROSS JOIN (VALUES ") + for i, md := range metadata { + if i > 0 { + builder.WriteString(", ") + } + builder.WriteRune('(') + // The complex placeholder handling is needed to append the type casts + // if we use WriteArgs directly, pgx resolves all args as TEXT instead of the correct type + placeholders := make([]string, 4) + placeholders[0] = builder.AppendArg(md.Key) + placeholders[1] = builder.AppendArg(md.Value) + "::BYTEA" + var createdAt, updatedAt any = database.NowInstruction, database.NullInstruction + if !md.CreatedAt.IsZero() { + createdAt = md.CreatedAt + } + if !md.UpdatedAt.IsZero() { + updatedAt = md.UpdatedAt + } + placeholders[2] = builder.AppendArg(createdAt) + "::TIMESTAMPTZ" + placeholders[3] = builder.AppendArg(updatedAt) + "::TIMESTAMPTZ" + builder.WriteString(strings.Join(placeholders, ", ")) + builder.WriteRune(')') + } + builder.WriteString(") AS md(key, value, created_at, updated_at) ON CONFLICT (") + database.Columns{ + u.instanceIDColumn(), + u.userIDColumn(), + u.keyColumn(), + }.WriteUnqualified(builder) + builder.WriteString(") DO UPDATE SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at WHERE ") + u.valueColumn().WriteQualified(builder) + builder.WriteString(" IS DISTINCT FROM EXCLUDED.value") + }, + nil, + ) +} + +// RemoveMetadata implements [domain.UserRepository]. +func (u userMetadata) RemoveMetadata(condition database.Condition) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM zitadel.user_metadata USING ") + builder.WriteString(existingUser.unqualifiedTableName()) + writeCondition(builder, database.And( + database.NewColumnCondition(existingUser.InstanceIDColumn(), u.instanceIDColumn()), + database.NewColumnCondition(existingUser.IDColumn(), u.userIDColumn()), + condition, + )) + }, + nil, + ) +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +func (u userMetadata) MetadataConditions() domain.UserMetadataConditions { + return u +} + +// MetadataKeyCondition implements [domain.UserMetadataConditions]. +func (u userMetadata) MetadataKeyCondition(op database.TextOperation, key string) database.Condition { + return database.NewTextCondition(u.keyColumn(), op, key) +} + +// MetadataValueCondition implements [domain.UserMetadataConditions]. +func (u userMetadata) MetadataValueCondition(op database.BytesOperation, value []byte) database.Condition { + return database.NewBytesCondition[[]byte](database.SHA256Column(u.valueColumn()), op, database.SHA256Value(value)) +} + +var _ domain.UserMetadataConditions = (*userMetadata)(nil) + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userMetadata) instanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} + +func (u userMetadata) userIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "user_id") +} + +func (u userMetadata) keyColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "key") +} + +func (u userMetadata) valueColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "value") +} diff --git a/backend/v3/storage/database/repository/user_passkey.go b/backend/v3/storage/database/repository/user_passkey.go new file mode 100644 index 000000000..b635cee41 --- /dev/null +++ b/backend/v3/storage/database/repository/user_passkey.go @@ -0,0 +1,264 @@ +package repository + +import ( + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/instrumentation/logging" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type userPasskey struct{} + +func (u userPasskey) unqualifiedTableName() string { + return "user_passkeys" +} + +func (u userPasskey) qualifiedTableName() string { + return "zitadel.user_passkeys" +} + +// ------------------------------------------------------------- +// changes +// ------------------------------------------------------------- + +// AddPasskey implements [domain.HumanUserRepository]. +func (u userPasskey) AddPasskey(passkey *domain.Passkey) database.Change { + var ( + createdAt any = database.NowInstruction + updatedAt any = database.NowInstruction + verifiedAt any = database.NowInstruction + ) + if !passkey.CreatedAt.IsZero() { + createdAt = passkey.CreatedAt + } + if !passkey.UpdatedAt.IsZero() { + updatedAt = passkey.UpdatedAt + } + if !passkey.VerifiedAt.IsZero() { + verifiedAt = passkey.VerifiedAt + } + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("INSERT INTO ") + builder.WriteString(u.qualifiedTableName()) + builder.WriteString(" (") + database.Columns{ + u.instanceIDColumn(), + u.userIDColumn(), + u.tokenIDColumn(), + u.keyIDColumn(), + u.createdAtColumn(), + u.updatedAtColumn(), + u.verifiedAtColumn(), + u.typeColumn(), + u.nameColumn(), + u.signCountColumn(), + u.challengeColumn(), + u.publicKeyColumn(), + u.attestationTypeColumn(), + u.authenticatorAttestationGUIDColumn(), + u.relyingPartyIDColumn(), + }.WriteUnqualified(builder) + builder.WriteString(") SELECT ") + database.Columns{ + existingHumanUser.InstanceIDColumn(), + existingHumanUser.IDColumn(), + }.WriteQualified(builder) + builder.WriteString(", ") + builder.WriteArgs( + passkey.ID, + passkey.KeyID, + createdAt, + updatedAt, + verifiedAt, + passkey.Type, + passkey.Name, + passkey.SignCount, + passkey.Challenge, + passkey.PublicKey, + passkey.AttestationType, + passkey.AuthenticatorAttestationGUID, + passkey.RelyingPartyID, + ) + builder.WriteString(" FROM ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + }, nil, + ) +} + +// RemovePasskey implements [domain.HumanUserRepository]. +func (u userPasskey) RemovePasskey(condition database.Condition) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM ") + builder.WriteString(u.qualifiedTableName()) + builder.WriteString(" USING ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + writeCondition(builder, database.And( + database.NewColumnCondition(existingHumanUser.InstanceIDColumn(), u.instanceIDColumn()), + database.NewColumnCondition(existingHumanUser.IDColumn(), u.userIDColumn()), + condition, + )) + }, + nil, + ) +} + +// UpdatePasskey implements [domain.HumanUserRepository]. +func (u userPasskey) UpdatePasskey(condition database.Condition, changes ...database.Change) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE ") + builder.WriteString(u.qualifiedTableName()) + builder.WriteString(" SET ") + err := database.Changes(changes).Write(builder) + logging.New(logging.StreamRuntime).Debug("write changes in cte failed", "error", err) + builder.WriteString(" FROM ") + builder.WriteString(existingHumanUser.unqualifiedTableName()) + writeCondition(builder, database.And( + database.NewColumnCondition(existingHumanUser.InstanceIDColumn(), u.instanceIDColumn()), + database.NewColumnCondition(existingHumanUser.IDColumn(), u.userIDColumn()), + condition, + )) + }, + nil, + ) +} + +// SetPasskeyAttestationType implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyAttestationType(attestationType string) database.Change { + return database.NewChange(u.attestationTypeColumn(), attestationType) +} + +// SetPasskeyAuthenticatorAttestationGUID implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyAuthenticatorAttestationGUID(aaguid []byte) database.Change { + return database.NewChange(u.authenticatorAttestationGUIDColumn(), aaguid) +} + +// SetPasskeyKeyID implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyKeyID(keyID []byte) database.Change { + return database.NewChange(u.keyIDColumn(), keyID) +} + +// SetPasskeyName implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyName(name string) database.Change { + return database.NewChange(u.nameColumn(), name) +} + +// SetPasskeyPublicKey implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyPublicKey(publicKey []byte) database.Change { + return database.NewChange(u.publicKeyColumn(), publicKey) +} + +// SetPasskeySignCount implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeySignCount(signCount uint32) database.Change { + return database.NewChange(u.signCountColumn(), signCount) +} + +// SetPasskeyUpdatedAt implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyUpdatedAt(updatedAt time.Time) database.Change { + return database.NewChange(u.updatedAtColumn(), updatedAt) +} + +// SetPasskeyVerifiedAt implements [domain.HumanUserRepository]. +func (u userPasskey) SetPasskeyVerifiedAt(verifiedAt time.Time) database.Change { + verifiedAtChange := database.NewChange(u.verifiedAtColumn(), database.NowInstruction) + if !verifiedAt.IsZero() { + verifiedAtChange = database.NewChange(u.verifiedAtColumn(), verifiedAt) + } + return verifiedAtChange +} + +// ------------------------------------------------------------- +// conditions +// ------------------------------------------------------------- + +// PasskeyConditions implements [domain.HumanUserRepository]. +func (u userPasskey) PasskeyConditions() domain.HumanPasskeyConditions { + return u +} + +// ChallengeCondition implements [domain.HumanPasskeyConditions]. +func (u userPasskey) ChallengeCondition(challenge []byte) database.Condition { + return database.NewBytesCondition[[]byte](database.SHA256Column(u.challengeColumn()), database.BytesOperationEqual, database.SHA256Value(challenge)) +} + +// IDCondition implements [domain.HumanPasskeyConditions]. +func (u userPasskey) IDCondition(passkeyID string) database.Condition { + return database.NewTextCondition(u.tokenIDColumn(), database.TextOperationEqual, passkeyID) +} + +// KeyIDCondition implements [domain.HumanPasskeyConditions]. +func (u userPasskey) KeyIDCondition(keyID string) database.Condition { + return database.NewTextCondition(u.keyIDColumn(), database.TextOperationEqual, keyID) +} + +// TypeCondition implements [domain.HumanPasskeyConditions]. +func (u userPasskey) TypeCondition(passkeyType domain.PasskeyType) database.Condition { + return database.NewTextCondition(u.typeColumn(), database.TextOperationEqual, passkeyType.String()) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (u userPasskey) tokenIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "token_id") +} + +func (u userPasskey) keyIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "key_id") +} + +func (u userPasskey) instanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} + +func (u userPasskey) userIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "user_id") +} + +func (u userPasskey) relyingPartyIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "relying_party_id") +} + +func (u userPasskey) authenticatorAttestationGUIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "authenticator_attestation_guid") +} + +func (u userPasskey) attestationTypeColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "attestation_type") +} + +func (u userPasskey) publicKeyColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "public_key") +} + +func (u userPasskey) challengeColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "challenge") +} + +func (u userPasskey) signCountColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "sign_count") +} + +func (u userPasskey) nameColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "name") +} + +func (u userPasskey) typeColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "type") +} + +func (u userPasskey) verifiedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "verified_at") +} + +func (u userPasskey) updatedAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "updated_at") +} + +func (u userPasskey) createdAtColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "created_at") +} diff --git a/backend/v3/storage/database/repository/user_personal_access_token.go b/backend/v3/storage/database/repository/user_personal_access_token.go new file mode 100644 index 000000000..0f9d3a043 --- /dev/null +++ b/backend/v3/storage/database/repository/user_personal_access_token.go @@ -0,0 +1,60 @@ +package repository + +import ( + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type userPersonalAccessToken struct{} + +// AddPersonalAccessToken implements [domain.MachineUserRepository]. +func (userPersonalAccessToken) AddPersonalAccessToken(pat *domain.PersonalAccessToken) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("INSERT INTO zitadel.user_personal_access_tokens (" + + "instance_id, user_id, id, created_at, expires_at, scopes" + + ") SELECT instance_id, id, ", + ) + var createdAt any = database.NowInstruction + if !pat.CreatedAt.IsZero() { + createdAt = pat.CreatedAt + } + var expiresAt any = database.NullInstruction + if !pat.ExpiresAt.IsZero() { + expiresAt = pat.ExpiresAt + } + builder.WriteArgs(pat.ID, createdAt, expiresAt, pat.Scopes) + builder.WriteString(" FROM existing_user") + }, + nil, + ) +} + +// RemovePersonalAccessToken implements [domain.MachineUserRepository]. +func (userPersonalAccessToken) RemovePersonalAccessToken(id string) database.Change { + return database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM zitadel.user_personal_access_tokens WHERE " + + "(instance_id, user_id, id) = (SELECT instance_id, id, ") + builder.WriteArg(id) + builder.WriteString(" FROM existing_user)") + }, + nil, + ) +} + +func (userPersonalAccessToken) qualifiedTableName() string { + return "zitadel.user_personal_access_tokens" +} + +func (userPersonalAccessToken) unqualifiedTableName() string { + return "user_personal_access_tokens" +} + +func (u userPersonalAccessToken) instanceIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "instance_id") +} + +func (u userPersonalAccessToken) userIDColumn() database.Column { + return database.NewColumn(u.unqualifiedTableName(), "user_id") +} diff --git a/backend/v3/storage/database/repository/user_test.go b/backend/v3/storage/database/repository/user_test.go new file mode 100644 index 000000000..c99bf3945 --- /dev/null +++ b/backend/v3/storage/database/repository/user_test.go @@ -0,0 +1,4187 @@ +package repository_test + +import ( + "context" + "encoding/json" + "fmt" + "slices" + "testing" + "time" + + "github.com/brianvoe/gofakeit/v6" + "github.com/muhlemmer/gu" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/crypto" +) + +func Test_user_Get(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + + instanceID := createInstance(t, tx) + + orgID1 := createOrganization(t, tx, instanceID) + orgID2 := createOrganization(t, tx, instanceID) + + now := time.Now().Round(time.Millisecond) + + human := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID1, + Username: "human-user", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Human: &domain.HumanUser{ + FirstName: "John", + LastName: "Doe", + DisplayName: "JohnD", + Nickname: "johnny", + PreferredLanguage: language.English, + Gender: domain.HumanGenderMale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "john@doe.com", + UnverifiedAddress: "john@doe.com", + VerifiedAt: now, + }, + }, + } + + machine := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID2, + Username: "machine-user", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Machine: &domain.MachineUser{ + Name: "My Machine", + Description: "This is my machine user", + }, + } + + err := userRepo.Create(t.Context(), tx, human) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, machine) + require.NoError(t, err) + + tests := []struct { + name string + setup func(t *testing.T, tx database.Transaction) error + condition database.Condition + wantErr error + expected *domain.User + }{ + { + name: "incomplete condition", + condition: userRepo.IDCondition(human.ID), + wantErr: database.NewMissingConditionError(userRepo.InstanceIDColumn()), + }, + { + name: "not found", + condition: userRepo.PrimaryKeyCondition(instanceID, "not-found"), + wantErr: new(database.NoRowFoundError), + }, + { + name: "too many", + condition: userRepo.InstanceIDCondition(instanceID), + wantErr: new(database.MultipleRowsFoundError), + }, + { + name: "get human", + condition: userRepo.PrimaryKeyCondition(instanceID, human.ID), + expected: human, + }, + { + name: "get machine", + condition: userRepo.PrimaryKeyCondition(instanceID, machine.ID), + expected: machine, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + if tt.setup != nil { + err := tt.setup(t, savepoint) + require.NoError(t, err) + } + + user, err := userRepo.Get(t.Context(), savepoint, database.WithCondition(tt.condition)) + require.ErrorIs(t, err, tt.wantErr) + + require.Equal(t, tt.expected == nil, user == nil) + if tt.expected == nil { + return + } + assertUser(t, tt.expected, user) + }) + } +} + +func Test_user_List(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + + instanceID := createInstance(t, tx) + + orgID1 := createOrganization(t, tx, instanceID) + orgID2 := createOrganization(t, tx, instanceID) + + idpID := createIdentityProvider(t, tx, instanceID, orgID1) + + now := time.Now().Round(time.Millisecond) + + human1 := &domain.User{ + ID: "h1", + InstanceID: instanceID, + OrganizationID: orgID1, + Username: "human-user-1", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Human: &domain.HumanUser{ + FirstName: "John", + LastName: "Doe", + DisplayName: "JohnD", + Nickname: "johnny", + PreferredLanguage: language.English, + Gender: domain.HumanGenderMale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "john@doe.com", + UnverifiedAddress: "john@doe.com", + VerifiedAt: now, + }, + Phone: &domain.HumanPhone{ + Number: "+1234567890", + UnverifiedNumber: "+1234567890", + VerifiedAt: now, + }, + Passkeys: []*domain.Passkey{ + { + ID: "passkey-1", + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + }, + Password: domain.HumanPassword{ + Hash: "password-hash", + ChangedAt: now, + }, + TOTP: &domain.HumanTOTP{ + VerifiedAt: now, + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + }, + IdentityProviderLinks: []*domain.IdentityProviderLink{ + { + ProviderID: idpID, + ProvidedUserID: "provided-id-h1", + ProvidedUsername: "provided-username-h1", + CreatedAt: now, + UpdatedAt: now, + }, + }, + Verifications: []*domain.Verification{ + { + ID: "h1-v1", + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("asdf"), + }, + CreatedAt: now, + UpdatedAt: now, + }, + }, + }, + Metadata: []*domain.Metadata{ + { + InstanceID: instanceID, + Key: "key1", + Value: []byte("asdf"), + CreatedAt: now, + UpdatedAt: now, + }, + { + InstanceID: instanceID, + Key: "key2", + Value: []byte("123"), + CreatedAt: now, + UpdatedAt: now, + }, + }, + } + human2 := &domain.User{ + ID: "h2", + InstanceID: instanceID, + OrganizationID: orgID1, + Username: "human-user-2", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Human: &domain.HumanUser{ + FirstName: "Jane", + LastName: "Doe", + DisplayName: "JaneD", + Nickname: "janie", + PreferredLanguage: language.English, + Gender: domain.HumanGenderFemale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + UnverifiedAddress: "jane@doe.com", + PendingVerification: &domain.Verification{ + ID: "h2-email", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("asdf"), + }, + }, + }, + Phone: &domain.HumanPhone{ + UnverifiedNumber: "+1234567890", + PendingVerification: &domain.Verification{ + ID: "h2-phone", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("asdf"), + }, + }, + }, + Passkeys: []*domain.Passkey{ + { + ID: "passkey-2", + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + Challenge: []byte("challenge2"), + KeyID: []byte("key-id3"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + }, + Password: domain.HumanPassword{ + PendingVerification: &domain.Verification{ + ID: "h2-pw", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("asdf"), + }, + }, + }, + TOTP: &domain.HumanTOTP{ + VerifiedAt: now, + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + }, + IdentityProviderLinks: []*domain.IdentityProviderLink{ + { + ProviderID: idpID, + ProvidedUserID: "provided-id-h2", + ProvidedUsername: "provided-username-h2", + CreatedAt: now, + UpdatedAt: now, + }, + }, + Verifications: []*domain.Verification{ + { + ID: "h2-v1", + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("asdf"), + }, + CreatedAt: now, + UpdatedAt: now, + }, + }, + }, + Metadata: []*domain.Metadata{ + { + InstanceID: instanceID, + Key: "key1", + Value: []byte("asdf"), + CreatedAt: now, + UpdatedAt: now, + }, + { + InstanceID: instanceID, + Key: "key2", + Value: []byte("123"), + CreatedAt: now, + UpdatedAt: now, + }, + }, + } + + machine1 := &domain.User{ + ID: "m1", + InstanceID: instanceID, + OrganizationID: orgID2, + Username: "machine-user-1", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Machine: &domain.MachineUser{ + Name: "My Machine", + Description: "This is my machine user", + Secret: "asdf", + AccessTokenType: domain.AccessTokenTypeJWT, + PATs: []*domain.PersonalAccessToken{ + { + ID: "pat1", + CreatedAt: now, + Scopes: []string{"scope1", "scope2"}, + }, + }, + Keys: []*domain.MachineKey{ + { + ID: "mk1", + PublicKey: []byte("asdf"), + CreatedAt: now, + Type: domain.MachineKeyTypeJSON, + }, + }, + }, + Metadata: []*domain.Metadata{ + { + InstanceID: instanceID, + Key: "key1", + Value: []byte("asdf"), + CreatedAt: now, + UpdatedAt: now, + }, + { + InstanceID: instanceID, + Key: "key2", + Value: []byte("123"), + CreatedAt: now, + UpdatedAt: now, + }, + }, + } + + machine2 := &domain.User{ + ID: "m2", + InstanceID: instanceID, + OrganizationID: orgID2, + Username: "machine-user-2", + State: domain.UserStateActive, + CreatedAt: now, + UpdatedAt: now, + Machine: &domain.MachineUser{ + Name: "My Machine 2", + Description: "This is my machine user 2", + Secret: "asdf", + AccessTokenType: domain.AccessTokenTypeJWT, + PATs: []*domain.PersonalAccessToken{ + { + ID: "pat2", + CreatedAt: now, + Scopes: []string{"scope1", "scope2"}, + }, + }, + Keys: []*domain.MachineKey{ + { + ID: "mk2", + PublicKey: []byte("asdf"), + CreatedAt: now, + Type: domain.MachineKeyTypeJSON, + }, + }, + }, + Metadata: []*domain.Metadata{ + { + InstanceID: instanceID, + Key: "key1", + Value: []byte("asdf"), + CreatedAt: now, + UpdatedAt: now, + }, + { + InstanceID: instanceID, + Key: "key2", + Value: []byte("123"), + CreatedAt: now, + UpdatedAt: now, + }, + }, + } + + err := userRepo.Create(t.Context(), tx, human1) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, human2) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, machine1) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, machine2) + require.NoError(t, err) + + tests := []struct { + name string + condition database.Condition + wantErr error + expected []*domain.User + }{ + { + name: "incomplete condition", + condition: userRepo.IDCondition(human1.ID), + wantErr: database.NewMissingConditionError(userRepo.InstanceIDColumn()), + }, + { + name: "get human 1", + condition: userRepo.PrimaryKeyCondition(instanceID, human1.ID), + expected: []*domain.User{human1}, + }, + { + name: "get humans", + condition: database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.TypeCondition(domain.UserTypeHuman), + ), + expected: []*domain.User{human1, human2}, + }, + { + name: "get machine 1", + condition: userRepo.PrimaryKeyCondition(instanceID, machine1.ID), + expected: []*domain.User{machine1}, + }, + { + name: "get machines", + condition: database.And( + userRepo.InstanceIDCondition(instanceID), + userRepo.TypeCondition(domain.UserTypeMachine), + ), + expected: []*domain.User{machine1, machine2}, + }, + { + name: "get all", + condition: userRepo.InstanceIDCondition(instanceID), + expected: []*domain.User{human1, human2, machine1, machine2}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotten, err := userRepo.List(t.Context(), tx, database.WithCondition(tt.condition), database.WithOrderByAscending(userRepo.UsernameColumn())) + require.ErrorIs(t, err, tt.wantErr) + if tt.wantErr != nil { + return + } + + assertUsers(t, tt.expected, gotten) + }) + } +} + +// Test_user_ListConditions verifies the correct behavior of all conditions. +func Test_user_ListConditions(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + humanRepo := repository.HumanUserRepository() + + instanceID1 := createInstance(t, tx) + instance1OrgID1 := createOrganization(t, tx, instanceID1) + instance1OrgID2 := createOrganization(t, tx, instanceID1) + idpID := createIdentityProvider(t, tx, instanceID1, instance1OrgID1) + + instanceID2 := createInstance(t, tx) + instance2OrgID1 := createOrganization(t, tx, instanceID2) + + now := time.Now().Round(time.Millisecond) + + users := []*domain.User{ + { + ID: "h1", + InstanceID: instanceID1, + OrganizationID: instance1OrgID1, + Username: "human-user-1", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "aa", + LastName: "bb", + DisplayName: "aa bb", + Nickname: "aabb", + PreferredLanguage: language.English, + Gender: domain.HumanGenderDiverse, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "aa.bb@example.com", + VerifiedAt: now, + }, + }, + }, + { + ID: "h2", + InstanceID: instanceID1, + OrganizationID: instance1OrgID2, + Username: "human-user-2", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "aaa", + LastName: "bbb", + DisplayName: "aaa bbb", + Nickname: "aaabbb", + PreferredLanguage: language.German, + Gender: domain.HumanGenderFemale, + Email: domain.HumanEmail{ + Address: "aaa@bbb.com", + }, + }, + }, + { + ID: "h3", + InstanceID: instanceID2, + OrganizationID: instance2OrgID1, + Username: "human-user-3", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "aaaa", + LastName: "bbbb", + DisplayName: "aaaa bbbb", + Email: domain.HumanEmail{ + Address: "aaaa.bbbb@example.com", + }, + }, + }, + { + ID: "m1", + InstanceID: instanceID1, + OrganizationID: instance1OrgID1, + Username: "machine-user-1", + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: "machine-user-1", + Description: "This is my machine user", + }, + }, + { + ID: "m2", + InstanceID: instanceID1, + OrganizationID: instance1OrgID2, + Username: "machine-user-2", + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: "machine-user-2", + }, + }, + { + ID: "m3", + InstanceID: instanceID2, + OrganizationID: instance2OrgID1, + Username: "machine-user-3", + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: "machine-user-3", + }, + }, + } + + for _, user := range users { + err := userRepo.Create(t.Context(), tx, user) + require.NoError(t, err) + } + + type want struct { + err error + ids []string + } + tests := []struct { + name string + setup func(t *testing.T, tx database.Transaction) + opts []database.QueryOption + want want + }{ + // user conditions + { + name: "by instance id", + opts: []database.QueryOption{ + database.WithCondition(userRepo.InstanceIDCondition(instanceID1)), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2", "m1", "m2"}, + }, + }, + { + name: "by primary key", + opts: []database.QueryOption{ + database.WithCondition(userRepo.PrimaryKeyCondition(instanceID1, "h1")), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by organization id", + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.OrganizationIDCondition(instance1OrgID1), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "m1"}, + }, + }, + { + name: "by id", + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.IDCondition("h1"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by username", + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.UsernameCondition(database.TextOperationStartsWith, "human-user-"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by state", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + userRepo.SetState(domain.UserStateInactive), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + userRepo.SetState(domain.UserStateInitial), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "m1"), + userRepo.SetState(domain.UserStateLocked), + ) + require.NoError(t, err) + + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.StateCondition(domain.UserStateActive), + )), + }, + want: want{ + err: nil, + ids: []string{"m2"}, + }, + }, + { + name: "by type", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.TypeCondition(domain.UserTypeMachine), + )), + }, + want: want{ + err: nil, + ids: []string{"m1", "m2"}, + }, + }, + // { + // name: "by login name", + // setup: func(t *testing.T, tx database.Transaction) { + // }, + // opts: []database.QueryOption{ + // database.WithCondition(database.And( + // userRepo.InstanceIDCondition(instanceID1), + // )), + // }, + // want: want{ + // err: nil, + // ids: []string{}, + // }, + // }, + + // metadata conditions + { + name: "by metadata key", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key1", + Value: []byte("value"), + }, + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.ExistsMetadata( + userRepo.MetadataConditions().MetadataKeyCondition(database.TextOperationEqual, "key1"), + ), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by metadata value", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key1", + Value: []byte("123"), + }, + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value1"), + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID2, "h3"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID2, + Key: "key2", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.ExistsMetadata( + userRepo.MetadataConditions().MetadataValueCondition(database.BytesOperationEqual, []byte("value")), + ), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by metadata key and value", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key1", + Value: []byte("123"), + }, + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID1, + Key: "key2", + Value: []byte("value1"), + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID2, "h3"), + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID2, + Key: "key1", + Value: []byte("value"), + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + userRepo.ExistsMetadata( + database.And( + userRepo.MetadataConditions().MetadataKeyCondition(database.TextOperationEqual, "key1"), + userRepo.MetadataConditions().MetadataValueCondition(database.BytesOperationEqual, []byte("123")), + ), + ), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + + // human conditions + { + name: "by display name", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.DisplayNameCondition(database.TextOperationEndsWith, " bbb"), + )), + }, + want: want{ + err: nil, + ids: []string{"h2"}, + }, + }, + { + name: "by first name", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.FirstNameCondition(database.TextOperationEqual, "aa"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by last name", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.LastNameCondition(database.TextOperationNotEqual, "bb"), + )), + }, + want: want{ + err: nil, + ids: []string{"h2"}, + }, + }, + { + name: "by nickname", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.NicknameCondition(database.TextOperationContains, "aabb"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by preferred language", + setup: func(t *testing.T, tx database.Transaction) { + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.PreferredLanguageCondition(language.German), + )), + }, + want: want{ + err: nil, + ids: []string{"h2"}, + }, + }, + + // phone conditions + { + name: "by phone", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.PhoneCondition(database.TextOperationEqual, "+1234567890"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by unverified phone", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.UnverifiedPhoneCondition(database.TextOperationEqual, "+1234567890"), + )), + }, + want: want{ + err: nil, + ids: []string{"h2"}, + }, + }, + { + name: "by verified phone", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.VerifiedPhoneCondition(database.TextOperationEqual, "+1234567890"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + + // human email conditions + { + name: "by email", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.EmailCondition(database.TextOperationEqual, "test@email.com"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by unverified email", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.UnverifiedEmailCondition(database.TextOperationEqual, "test@email.com"), + )), + }, + want: want{ + err: nil, + ids: []string{"h2"}, + }, + }, + { + name: "by verified email", + setup: func(t *testing.T, tx database.Transaction) { + _, err := humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.SetEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeSkipped{}), + ) + require.NoError(t, err) + _, err = humanRepo.Update(t.Context(), tx, humanRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.SetUnverifiedEmail("test@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + Expiry: gu.Ptr(5 * time.Minute), + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.VerifiedEmailCondition(database.TextOperationEqual, "test@email.com"), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + + // passkey conditions + { + name: "by passkey same challenge", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-1", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-2", + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-3", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsPasskey(humanRepo.PasskeyConditions().ChallengeCondition([]byte("challenge1"))), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by passkey challenge", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-1", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-2", + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-3", + Challenge: []byte("challenge3"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsPasskey(humanRepo.PasskeyConditions().ChallengeCondition([]byte("challenge1"))), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by passkey id", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-1", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-2", + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-3", + Challenge: []byte("challenge3"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsPasskey(humanRepo.PasskeyConditions().IDCondition("passkey-1")), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by passkey key id", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-1", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-2", + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-3", + Challenge: []byte("challenge3"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsPasskey(humanRepo.PasskeyConditions().KeyIDCondition("key-id")), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by passkey type", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-1", + Challenge: []byte("challenge1"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-2", + Challenge: []byte("challenge2"), + KeyID: []byte("key-id2"), + Name: "u2f", + Type: domain.PasskeyTypeU2F, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddPasskey( + &domain.Passkey{ + ID: "passkey-3", + Challenge: []byte("challenge3"), + KeyID: []byte("key-id"), + Name: "passwordless", + Type: domain.PasskeyTypePasswordless, + PublicKey: []byte("public key"), + AuthenticatorAttestationGUID: []byte("aaguid"), + AttestationType: "attestation type", + RelyingPartyID: "rpid", + }, + ), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsPasskey(humanRepo.PasskeyConditions().TypeCondition(domain.PasskeyTypeU2F)), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + + // idp link conditions + { + name: "by idp link provider id", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1", + ProvidedUsername: "provided-username-h1", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1-2", + ProvidedUsername: "provided-username-h1-2", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h2-2", + ProvidedUsername: "provided-username-h2-2", + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsIdentityProviderLink(humanRepo.IdentityProviderLinkConditions().ProviderIDCondition(idpID)), + )), + }, + want: want{ + err: nil, + ids: []string{"h1", "h2"}, + }, + }, + { + name: "by idp link provided user id", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1", + ProvidedUsername: "provided-username-h1", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1-2", + ProvidedUsername: "provided-username-h1-2", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h2-2", + ProvidedUsername: "provided-username-h2-2", + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsIdentityProviderLink(humanRepo.IdentityProviderLinkConditions().ProvidedUserIDCondition("provided-id-h1-2")), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + { + name: "by idp link username", + setup: func(t *testing.T, tx database.Transaction) { + _, err := userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1", + ProvidedUsername: "provided-username-h1", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h1"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h1-2", + ProvidedUsername: "provided-username-h1", + }), + ) + require.NoError(t, err) + _, err = userRepo.Update(t.Context(), tx, userRepo.PrimaryKeyCondition(instanceID1, "h2"), + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: idpID, + ProvidedUserID: "provided-id-h2-2", + ProvidedUsername: "provided-username-h2-2", + }), + ) + require.NoError(t, err) + }, + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition(instanceID1), + humanRepo.ExistsIdentityProviderLink(humanRepo.IdentityProviderLinkConditions().ProvidedUsernameCondition(database.TextOperationStartsWith, "provided-username-h1")), + )), + }, + want: want{ + err: nil, + ids: []string{"h1"}, + }, + }, + + { + name: "list none", + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.InstanceIDCondition("non-existing-instance-id"), + )), + }, + want: want{ + err: nil, + ids: []string{}, + }, + }, + { + name: "missing instance id condition", + opts: []database.QueryOption{ + database.WithCondition(database.And( + userRepo.IDCondition("h1"), + )), + }, + want: want{ + err: database.NewMissingConditionError(userRepo.InstanceIDColumn()), + ids: []string{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + if tt.setup != nil { + tt.setup(t, savepoint) + } + + gotten, err := userRepo.List(t.Context(), savepoint, append(tt.opts, database.WithOrderByAscending(userRepo.IDColumn()))...) + require.ErrorIs(t, err, tt.want.err) + if tt.want.err != nil { + return + } + + require.Len(t, gotten, len(tt.want.ids)) + for i, expectedID := range tt.want.ids { + assert.Equal(t, expectedID, gotten[i].ID) + } + }) + } +} + +func Test_user_Update(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + + humanRepo := repository.HumanUserRepository() + machineRepo := repository.MachineUserRepository() + + instanceID := createInstance(t, tx) + + orgID1 := createOrganization(t, tx, instanceID) + orgID2 := createOrganization(t, tx, instanceID) + + human := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID1, + Username: "human-user", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "John", + LastName: "Doe", + DisplayName: "JohnD", + Nickname: "johnny", + PreferredLanguage: language.English, + Gender: domain.HumanGenderMale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "john@doe.com", + }, + }, + } + humanCondition := userRepo.PrimaryKeyCondition(instanceID, human.ID) + + machine := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID2, + Username: "machine-user", + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: "My Machine", + Description: "This is my machine user", + }, + } + machineCondition := userRepo.PrimaryKeyCondition(instanceID, machine.ID) + + err := userRepo.Create(t.Context(), tx, human) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, machine) + require.NoError(t, err) + + now := time.Now().Round(time.Millisecond) + + type args struct { + condition database.Condition + changes []database.Change + } + type want struct { + err error + user *domain.User + } + type test struct { + name string + setup func(t *testing.T, tx database.QueryExecutor) error + args args + want want + } + tests := []test{ + { + name: "set username", + args: args{ + condition: humanCondition, + changes: []database.Change{ + userRepo.SetUsername("new-human-username"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Username = "new-human-username" + return u + }(), + }, + }, + { + name: "set state", + args: args{ + condition: machineCondition, + changes: []database.Change{ + userRepo.SetState(domain.UserStateInactive), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.State = domain.UserStateInactive + return u + }(), + }, + }, + { + name: "add metadata", + args: args{ + condition: machineCondition, + changes: []database.Change{ + userRepo.SetMetadata( + &domain.Metadata{ + InstanceID: instanceID, + Key: "key1", + Value: []byte("value"), + }, + &domain.Metadata{ + InstanceID: instanceID, + Key: "key2", + Value: []byte("42"), + }, + ), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Metadata = append(u.Metadata, &domain.Metadata{ + InstanceID: instanceID, + Key: "key1", + Value: []byte("value"), + }, &domain.Metadata{ + InstanceID: instanceID, + Key: "key2", + Value: []byte("42"), + }) + return u + }(), + }, + }, + { + name: "remove metadata", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, machineCondition, + userRepo.SetMetadata(&domain.Metadata{ + InstanceID: instanceID, + Key: "key1", + Value: []byte("value"), + }), + ) + return err + }, + args: args{ + condition: machineCondition, + changes: []database.Change{ + userRepo.RemoveMetadata(userRepo.MetadataConditions().MetadataKeyCondition(database.TextOperationEqual, "key1")), + }, + }, + want: want{ + user: machine, + }, + }, + { + name: "set human first name", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetFirstName("Joanne"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.FirstName = "Joanne" + return u + }(), + }, + }, + { + name: "set human last name", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetLastName("Known"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.LastName = "Known" + return u + }(), + }, + }, + { + name: "set human nickname", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetNickname("Ghost"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Nickname = "Ghost" + return u + }(), + }, + }, + { + name: "set human display name", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetDisplayName("Johnny the Ghost"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.DisplayName = "Johnny the Ghost" + return u + }(), + }, + }, + { + name: "set human preferred language", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPreferredLanguage(language.Afrikaans), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.PreferredLanguage = language.Afrikaans + return u + }(), + }, + }, + { + name: "set human gender", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetGender(domain.HumanGenderMale), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Gender = domain.HumanGenderMale + return u + }(), + }, + }, + { + name: "set human avatar key", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetAvatarKey(gu.Ptr("https://new.avatar/key")), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.AvatarKey = "https://new.avatar/key" + return u + }(), + }, + }, + { + name: "set human skip mfa initialization at", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SkipMultifactorInitializationAt(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.MultifactorInitializationSkippedAt = now + return u + }(), + }, + }, + { + name: "set human verification init", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ID: gu.Ptr("verification-id"), + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Verifications = append(u.Human.Verifications, &domain.Verification{ + ID: "verification-id", + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }) + return u + }(), + }, + }, + { + name: "set human verification update", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ID: gu.Ptr("verification-id"), + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetVerification(&domain.VerificationTypeUpdate{ + ID: gu.Ptr("verification-id"), + Expiry: gu.Ptr(10 * time.Minute), + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Verifications = append(u.Human.Verifications, &domain.Verification{ + ID: "verification-id", + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ExpiresAt: gu.Ptr(now.Add(10 * time.Minute)), + }) + return u + }(), + }, + }, + { + name: "set human verification verified", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ID: gu.Ptr("verification-id"), + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetVerification(&domain.VerificationTypeSucceeded{ + ID: gu.Ptr("verification-id"), + VerifiedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Verifications = nil + return u + }(), + }, + }, + { + name: "set human verification failed", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ID: gu.Ptr("verification-id"), + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetVerification(&domain.VerificationTypeFailed{ + ID: gu.Ptr("verification-id"), + FailedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Verifications = append(u.Human.Verifications, &domain.Verification{ + ID: "verification-id", + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + UpdatedAt: now, + FailedAttempts: 1, + }) + return u + }(), + }, + }, + { + name: "set human password verification init", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("pw-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + Expiry: gu.Ptr(24 * time.Hour), + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.PendingVerification = &domain.Verification{ + ID: "pw-id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + ExpiresAt: gu.Ptr(now.Add(24 * time.Hour)), + } + return u + }(), + }, + }, + { + name: "set human password verification update", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("pw-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeUpdate{ + Expiry: gu.Ptr(48 * time.Hour), + UpdatedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.PendingVerification = &domain.Verification{ + ID: "pw-id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + ExpiresAt: gu.Ptr(now.Add(48 * time.Hour)), + } + return u + }(), + }, + }, + { + name: "set human password verified", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("pw-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: now, + }), + humanRepo.SetPassword("hashedPassword"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password = domain.HumanPassword{ + Hash: "hashedPassword", + IsChangeRequired: false, + ChangedAt: now, + } + return u + }(), + }, + }, + { + name: "set human password failed", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("pw-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetResetPasswordVerification(&domain.VerificationTypeFailed{ + FailedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.PendingVerification = &domain.Verification{ + ID: "pw-id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedPassword"), + }, + FailedAttempts: 1, + } + return u + }(), + }, + }, + { + name: "set human password change required", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPasswordChangeRequired(true), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.IsChangeRequired = true + return u + }(), + }, + }, + { + name: "set human last successful password check", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetLastSuccessfulPasswordCheck(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.LastSuccessfullyCheckedAt = &now + return u + }(), + }, + }, + { + name: "set human increment password failed attempts", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.IncrementPasswordFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.FailedAttempts++ + return u + }(), + }, + }, + { + name: "set human reset password failed attempts", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.IncrementPasswordFailedAttempts(), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.ResetPasswordFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Password.FailedAttempts = 0 + return u + }(), + }, + }, + { + name: "set human set email verification init", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetUnverifiedEmail("new@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("verification-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.UnverifiedAddress = "new@email.com" + u.Human.Email.PendingVerification = &domain.Verification{ + ID: "verification-id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + } + return u + }(), + }, + }, + { + name: "set human set email verified", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetUnverifiedEmail("new@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("pw-id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetEmailVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.PendingVerification = nil + u.Human.Email.Address = "new@email.com" + u.Human.Email.UnverifiedAddress = "new@email.com" + u.Human.Email.VerifiedAt = now + return u + }(), + }, + }, + { + name: "set human set email verification update", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + humanRepo.SetUnverifiedEmail("new@email.com"), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetEmailVerification(&domain.VerificationTypeUpdate{ + Expiry: gu.Ptr(10 * time.Minute), + UpdatedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.UnverifiedAddress = "new@email.com" + u.Human.Email.PendingVerification = &domain.Verification{ + ID: "id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ExpiresAt: gu.Ptr(now.Add(10 * time.Minute)), + } + return u + }(), + }, + }, + { + name: "set human set email verification skipped", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetEmail("new@email.com"), + humanRepo.SetEmailVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.PendingVerification = nil + u.Human.Email.Address = "new@email.com" + u.Human.Email.VerifiedAt = now + return u + }(), + }, + }, + { + name: "set human set email verification failed", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetEmailVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + humanRepo.SetUnverifiedEmail("new@email.com"), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetEmailVerification(&domain.VerificationTypeFailed{ + FailedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.UnverifiedAddress = "new@email.com" + u.Human.Email.PendingVerification = &domain.Verification{ + ID: "id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + FailedAttempts: 1, + } + return u + }(), + }, + }, + { + name: "set human enable email otp at", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.EnableEmailOTPAt(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + assert.False(t, u.Human.Email.OTP.EnabledAt.Equal(now)) + u.Human.Email.OTP.EnabledAt = now + return u + }(), + }, + }, + { + name: "set human disable email otp", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.DisableEmailOTP(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.OTP.EnabledAt = time.Time{} + return u + }(), + }, + }, + { + name: "set human last successful email otp check", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetLastSuccessfulEmailOTPCheck(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + if u.Human.Email.OTP.LastSuccessfullyCheckedAt != nil { + assert.False(t, u.Human.Email.OTP.LastSuccessfullyCheckedAt.Equal(now)) + } + u.Human.Email.OTP.LastSuccessfullyCheckedAt = &now + return u + }(), + }, + }, + { + name: "set human increment email otp failed attempts", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.IncrementEmailOTPFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.OTP.FailedAttempts = 1 + return u + }(), + }, + }, + { + name: "set human reset email otp failed attempts", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, humanCondition, humanRepo.IncrementEmailOTPFailedAttempts()) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.ResetEmailOTPFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Email.OTP.FailedAttempts = 0 + return u + }(), + }, + }, + { + name: "set human set phone verification init", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + humanRepo.SetUnverifiedPhone("+0000000000"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + UnverifiedNumber: "+0000000000", + PendingVerification: &domain.Verification{ + ID: "id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }, + } + return u + }(), + }, + }, + { + name: "set human set phone verified", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{}), + humanRepo.SetUnverifiedPhone("+1234567890"), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPhoneVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + UnverifiedNumber: "+1234567890", + VerifiedAt: now, + } + return u + }(), + }, + }, + { + name: "set human set phone verification update", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhoneVerification(&domain.VerificationTypeInit{ + ID: gu.Ptr("id"), + CreatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + }), + humanRepo.SetUnverifiedPhone("+1234567890"), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPhoneVerification(&domain.VerificationTypeUpdate{ + Expiry: gu.Ptr(10 * time.Minute), + UpdatedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + UnverifiedNumber: "+1234567890", + PendingVerification: &domain.Verification{ + ID: "id", + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "sha-1", + KeyID: "keyID", + Crypted: []byte("cryptedCode"), + }, + ExpiresAt: gu.Ptr(now.Add(10 * time.Minute)), + }, + } + return u + }(), + }, + }, + { + name: "set human set phone verification skipped", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + } + return u + }(), + }, + }, + { + name: "set human enable sms otp at", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.EnableSMSOTPAt(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + OTP: domain.OTP{ + EnabledAt: now, + }, + } + return u + }(), + }, + }, + { + name: "set human disable sms otp", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + humanRepo.EnableSMSOTPAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.DisableSMSOTP(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + } + return u + }(), + }, + }, + { + name: "set human last successful sms otp check", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + humanRepo.EnableSMSOTPAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetLastSuccessfulSMSOTPCheck(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + OTP: domain.OTP{ + EnabledAt: now, + LastSuccessfullyCheckedAt: &now, + }, + } + return u + }(), + }, + }, + { + name: "set human increment sms otp failed attempts", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + humanRepo.EnableSMSOTPAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.IncrementSMSOTPFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + OTP: domain.OTP{ + EnabledAt: now, + FailedAttempts: 1, + }, + } + return u + }(), + }, + }, + { + name: "set human reset sms otp failed attempts", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + humanRepo.EnableSMSOTPAt(now), + ) + if err != nil { + return err + } + _, err = userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.IncrementSMSOTPFailedAttempts(), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.ResetSMSOTPFailedAttempts(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = &domain.HumanPhone{ + Number: "+1234567890", + VerifiedAt: now, + OTP: domain.OTP{ + EnabledAt: now, + FailedAttempts: 0, + }, + } + return u + }(), + }, + }, + { + name: "set human remove phone", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetPhone("+1234567890"), + humanRepo.SetPhoneVerification(&domain.VerificationTypeSkipped{ + SkippedAt: now, + }), + humanRepo.EnableSMSOTPAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.RemovePhone(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Phone = nil + return u + }(), + }, + }, + { + name: "set human totp secret", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetTOTPSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("new-secret"), + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.TOTP = &domain.HumanTOTP{ + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("new-secret"), + }, + } + return u + }(), + }, + }, + { + name: "set human totp verified at", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetTOTPSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetTOTPVerifiedAt(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.TOTP = &domain.HumanTOTP{ + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + VerifiedAt: now, + } + return u + }(), + }, + }, + { + name: "set human remove totp", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetTOTPSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }), + humanRepo.SetTOTPVerifiedAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.RemoveTOTP(), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.TOTP = nil + return u + }(), + }, + }, + { + name: "set human last successful totp check", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := humanRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetTOTPSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }), + humanRepo.SetTOTPVerifiedAt(now), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetLastSuccessfulTOTPCheck(now), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.TOTP = &domain.HumanTOTP{ + Secret: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "aes-256", + KeyID: "id", + Crypted: []byte("secret"), + }, + VerifiedAt: now, + LastSuccessfullyCheckedAt: &now, + } + return u + }(), + }, + }, + { + name: "set human add identity provider link", + setup: func(t *testing.T, tx database.QueryExecutor) error { + return repository.IDProviderRepository().Create(t.Context(), tx, &domain.IdentityProvider{ + InstanceID: instanceID, + OrgID: &orgID1, + ID: "provider-id", + Name: "idp", + State: domain.IDPStateActive, + Payload: json.RawMessage("{}"), + }) + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: "provider-id", + ProvidedUserID: "provided-user-id", + ProvidedUsername: "provided-username", + CreatedAt: now, + UpdatedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.IdentityProviderLinks = []*domain.IdentityProviderLink{ + { + ProviderID: "provider-id", + ProvidedUserID: "provided-user-id", + ProvidedUsername: "provided-username", + CreatedAt: now, + UpdatedAt: now, + }, + } + return u + }(), + }, + }, + { + name: "set human update identity provider link", + setup: func(t *testing.T, tx database.QueryExecutor) error { + err := repository.IDProviderRepository().Create(t.Context(), tx, &domain.IdentityProvider{ + InstanceID: instanceID, + OrgID: &orgID1, + ID: "provider-id", + Name: "idp", + State: domain.IDPStateActive, + Payload: json.RawMessage("{}"), + }) + if err != nil { + return err + } + _, err = userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: "provider-id", + ProvidedUserID: "provided-user-id", + ProvidedUsername: "provided-username", + CreatedAt: now, + UpdatedAt: now, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.UpdateIdentityProviderLink( + database.And( + humanRepo.IdentityProviderLinkConditions().ProviderIDCondition("provider-id"), + ), + humanRepo.SetIdentityProviderLinkUsername("updated-username"), + humanRepo.SetIdentityProviderLinkProvidedID("new-id"), + ), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.IdentityProviderLinks = []*domain.IdentityProviderLink{ + { + ProviderID: "provider-id", + ProvidedUserID: "new-id", + ProvidedUsername: "updated-username", + CreatedAt: now, + UpdatedAt: now, + }, + } + return u + }(), + }, + }, + { + name: "set human remove identity provider link", + setup: func(t *testing.T, tx database.QueryExecutor) error { + err := repository.IDProviderRepository().Create(t.Context(), tx, &domain.IdentityProvider{ + InstanceID: instanceID, + OrgID: &orgID1, + ID: "provider-id", + Name: "idp", + State: domain.IDPStateActive, + Payload: json.RawMessage("{}"), + }) + if err != nil { + return err + } + _, err = userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: "provider-id", + ProvidedUserID: "provided-user-id", + ProvidedUsername: "provided-username", + CreatedAt: now, + UpdatedAt: now, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.RemoveIdentityProviderLink("provider-id", "provided-user-id"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.IdentityProviderLinks = []*domain.IdentityProviderLink{} + return u + }(), + }, + }, + { + name: "set human add passkey", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.AddPasskey(&domain.Passkey{ + ID: "passkey-id", + PublicKey: []byte("public-key"), + Type: domain.PasskeyTypePasswordless, + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + KeyID: []byte("keyID"), + Name: "name", + AttestationType: "attestation-type", + AuthenticatorAttestationGUID: []byte("aa guid"), + Challenge: []byte("challenge"), + RelyingPartyID: "rp id", + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Passkeys = append(u.Human.Passkeys, &domain.Passkey{ + ID: "passkey-id", + PublicKey: []byte("public-key"), + Type: domain.PasskeyTypePasswordless, + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + KeyID: []byte("keyID"), + Name: "name", + AttestationType: "attestation-type", + AuthenticatorAttestationGUID: []byte("aa guid"), + Challenge: []byte("challenge"), + RelyingPartyID: "rp id", + }) + return u + }(), + }, + }, + { + name: "set human update passkey", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.AddPasskey(&domain.Passkey{ + ID: "passkey-id", + PublicKey: []byte("public-key"), + Type: domain.PasskeyTypePasswordless, + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + KeyID: []byte("keyID"), + Name: "name", + AttestationType: "attestation-type", + AuthenticatorAttestationGUID: []byte("aa guid"), + Challenge: []byte("challenge"), + RelyingPartyID: "rp id", + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.UpdatePasskey( + database.And( + humanRepo.PasskeyConditions().IDCondition("passkey-id"), + ), + humanRepo.SetPasskeyAttestationType("new-attestation-type"), + humanRepo.SetPasskeyAuthenticatorAttestationGUID([]byte("new-aaguid")), + humanRepo.SetPasskeyKeyID([]byte("new-key-id")), + humanRepo.SetPasskeyName("updated-name"), + humanRepo.SetPasskeyPublicKey([]byte("new-public-key")), + humanRepo.SetPasskeySignCount(123), + humanRepo.SetPasskeyUpdatedAt(now.Add(500*time.Millisecond)), + ), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Passkeys = []*domain.Passkey{ + { + ID: "passkey-id", + PublicKey: []byte("new-public-key"), + Type: domain.PasskeyTypePasswordless, + CreatedAt: now, + UpdatedAt: now.Add(500 * time.Millisecond), + VerifiedAt: now, + KeyID: []byte("new-key-id"), + Name: "updated-name", + AttestationType: "new-attestation-type", + AuthenticatorAttestationGUID: []byte("new-aaguid"), + Challenge: []byte("challenge"), + RelyingPartyID: "rp id", + SignCount: 123, + }, + } + return u + }(), + }, + }, + { + name: "set human remove passkey", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.AddPasskey(&domain.Passkey{ + ID: "passkey-id", + PublicKey: []byte("public-key"), + Type: domain.PasskeyTypePasswordless, + CreatedAt: now, + UpdatedAt: now, + VerifiedAt: now, + KeyID: []byte("keyID"), + Name: "name", + AttestationType: "attestation-type", + AuthenticatorAttestationGUID: []byte("aa guid"), + Challenge: []byte("challenge"), + RelyingPartyID: "rp id", + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.RemovePasskey(humanRepo.PasskeyConditions().IDCondition("passkey-id")), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Passkeys = nil + return u + }(), + }, + }, + { + name: "set machine name", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.SetName("Updated Machine"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.Name = "Updated Machine" + return u + }(), + }, + }, + { + name: "set machine description", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.SetDescription("Updated description"), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.Description = "Updated description" + return u + }(), + }, + }, + { + name: "set machine set secret", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.SetSecret(gu.Ptr("new-secret")), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.Secret = "new-secret" + return u + }(), + }, + }, + { + name: "set machine access token type", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.SetAccessTokenType(domain.AccessTokenTypeJWT), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.AccessTokenType = domain.AccessTokenTypeJWT + return u + }(), + }, + }, + func() test { + key := &domain.MachineKey{ + ID: gofakeit.UUID(), + PublicKey: []byte("public-key"), + Type: domain.MachineKeyTypeJSON, + ExpiresAt: now.Add(24 * time.Hour), + CreatedAt: now, + } + return test{ + name: "set machine add key", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.AddKey(key), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.Keys = append(u.Machine.Keys, key) + return u + }(), + }, + } + }(), + { + name: "set machine remove key", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := userRepo.Update(t.Context(), tx, machineCondition, + machineRepo.AddKey(&domain.MachineKey{ + ID: "key-to-remove", + PublicKey: []byte("public-key"), + Type: domain.MachineKeyTypeJSON, + ExpiresAt: now.Add(24 * time.Hour), + CreatedAt: now, + }), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.RemoveKey("key-to-remove"), + }, + }, + want: want{ + user: machine, + }, + }, + func() test { + pat := &domain.PersonalAccessToken{ + ID: gofakeit.UUID(), + Scopes: []string{"openid", "profile"}, + ExpiresAt: now.Add(24 * time.Hour), + CreatedAt: now, + } + return test{ + name: "set machine add personal access token", + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.AddPersonalAccessToken(pat), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(machineCondition)) + require.NoError(t, err) + u.Machine.PATs = append(u.Machine.PATs, pat) + return u + }(), + }, + } + }(), + { + name: "set machine remove personal access token", + setup: func(t *testing.T, tx database.QueryExecutor) error { + rowCount, err := machineRepo.Update(t.Context(), tx, machineCondition, + machineRepo.AddPersonalAccessToken(&domain.PersonalAccessToken{ + ID: "pat-to-remove", + Scopes: []string{"openid", "profile"}, + ExpiresAt: now.Add(24 * time.Hour), + CreatedAt: now, + }), + ) + assert.EqualValues(t, 1, rowCount) + return err + }, + args: args{ + condition: machineCondition, + changes: []database.Change{ + machineRepo.RemovePersonalAccessToken("pat-to-remove"), + }, + }, + want: want{ + user: machine, + }, + }, + { + name: "init invite verification", + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetInviteVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Expiry: gu.Ptr(10 * time.Minute), + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Invite = &domain.HumanInvite{ + PendingVerification: &domain.Verification{ + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + ExpiresAt: gu.Ptr(now.Add(10 * time.Minute)), + }, + } + return u + }(), + }, + }, + { + name: "invite verification succeeded", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetInviteVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Expiry: gu.Ptr(10 * time.Minute), + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetInviteVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: now, + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Invite = &domain.HumanInvite{ + PendingVerification: nil, + AcceptedAt: now, + } + return u + }(), + }, + }, + { + name: "invite verification updated", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetInviteVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Expiry: gu.Ptr(10 * time.Minute), + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetInviteVerification(&domain.VerificationTypeUpdate{ + Expiry: gu.Ptr(1 * time.Hour), + }), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Invite = &domain.HumanInvite{ + PendingVerification: &domain.Verification{ + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + ExpiresAt: gu.Ptr(now.Add(1 * time.Hour)), + }, + } + return u + }(), + }, + }, + { + name: "invite verification failed", + setup: func(t *testing.T, tx database.QueryExecutor) error { + _, err := userRepo.Update(t.Context(), tx, humanCondition, + humanRepo.SetInviteVerification(&domain.VerificationTypeInit{ + CreatedAt: now, + Expiry: gu.Ptr(10 * time.Minute), + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + }), + ) + return err + }, + args: args{ + condition: humanCondition, + changes: []database.Change{ + humanRepo.SetInviteVerification(&domain.VerificationTypeFailed{}), + }, + }, + want: want{ + user: func() *domain.User { + u, err := userRepo.Get(t.Context(), tx, database.WithCondition(humanCondition)) + require.NoError(t, err) + u.Human.Invite = &domain.HumanInvite{ + FailedAttempts: 1, + PendingVerification: &domain.Verification{ + CreatedAt: now, + UpdatedAt: now, + Code: &crypto.CryptoValue{ + CryptoType: crypto.TypeHash, + Algorithm: "aes-256", + KeyID: "key-id", + Crypted: []byte("invite code"), + }, + ExpiresAt: gu.Ptr(now.Add(1 * time.Hour)), + }, + } + return u + }(), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + + if tt.setup != nil { + err := tt.setup(t, savepoint) + require.NoError(t, err) + } + + _, err = userRepo.Update(t.Context(), savepoint, tt.args.condition, tt.args.changes...) + require.ErrorIs(t, err, tt.want.err) + + user, err := userRepo.Get(t.Context(), savepoint, database.WithCondition(tt.args.condition)) + require.NoError(t, err) + assertUser(t, tt.want.user, user) + }) + } +} + +func Test_user_Delete(t *testing.T) { + tx, rollback := transactionForRollback(t) + t.Cleanup(rollback) + + userRepo := repository.UserRepository() + + instanceID := createInstance(t, tx) + instance2ID := createInstance(t, tx) + + orgID1 := createOrganization(t, tx, instanceID) + orgID2 := createOrganization(t, tx, instanceID) + orgID3 := createOrganization(t, tx, instance2ID) + + human := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID1, + Username: "human-user", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "John", + LastName: "Doe", + DisplayName: "JohnD", + Nickname: "johnny", + PreferredLanguage: language.English, + Gender: domain.HumanGenderMale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "john@doe.com", + }, + }, + } + humanCondition := userRepo.PrimaryKeyCondition(instanceID, human.ID) + + machine := &domain.User{ + ID: gofakeit.UUID(), + InstanceID: instanceID, + OrganizationID: orgID2, + Username: "machine-user", + State: domain.UserStateActive, + Machine: &domain.MachineUser{ + Name: "My Machine", + Description: "This is my machine user", + }, + } + machineCondition := userRepo.PrimaryKeyCondition(instanceID, machine.ID) + + instance2Human := &domain.User{ + ID: human.ID, + InstanceID: instance2ID, + OrganizationID: orgID3, + Username: "human-user", + State: domain.UserStateActive, + Human: &domain.HumanUser{ + FirstName: "John", + LastName: "Doe", + DisplayName: "JohnD", + Nickname: "johnny", + PreferredLanguage: language.English, + Gender: domain.HumanGenderMale, + AvatarKey: "https://my.avatar/key", + Email: domain.HumanEmail{ + Address: "john@doe.com", + }, + }, + } + + err := userRepo.Create(t.Context(), tx, human) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, instance2Human) + require.NoError(t, err) + err = userRepo.Create(t.Context(), tx, machine) + require.NoError(t, err) + + type args struct { + condition database.Condition + } + type want struct { + err error + rowCount int + } + type test struct { + name string + setup func(t *testing.T, tx database.QueryExecutor) error + args args + want want + } + tests := []test{ + { + name: "delete human user", + args: args{ + condition: humanCondition, + }, + want: want{ + err: nil, + rowCount: 1, + }, + }, + { + name: "delete machine user", + args: args{ + condition: machineCondition, + }, + want: want{ + err: nil, + rowCount: 1, + }, + }, + { + name: "delete non-existing user", + args: args{ + condition: userRepo.PrimaryKeyCondition(instanceID, "non-existing-id"), + }, + want: want{ + err: nil, + rowCount: 0, + }, + }, + { + name: "wrong condition", + args: args{ + condition: userRepo.TypeCondition(domain.UserTypeHuman), + }, + want: want{ + err: new(database.MissingConditionError), + rowCount: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + savepoint, err := tx.Begin(t.Context()) + require.NoError(t, err) + t.Cleanup(func() { + err := savepoint.Rollback(context.Background()) + if err != nil { + t.Log("rollback savepoint failed", err) + } + }) + + if tt.setup != nil { + err := tt.setup(t, savepoint) + require.NoError(t, err) + } + + rowCount, err := userRepo.Delete(t.Context(), savepoint, tt.args.condition) + require.ErrorIs(t, err, tt.want.err) + assert.EqualValues(t, tt.want.rowCount, rowCount) + if tt.want.err != nil { + return + } + + user, err := userRepo.Get(t.Context(), savepoint, database.WithCondition(tt.args.condition)) + assert.ErrorIs(t, err, new(database.NoRowFoundError)) + assert.Nil(t, user) + }) + } +} + +func assertUsers(t *testing.T, expected, actual []*domain.User) { + t.Helper() + require.Len(t, actual, len(expected)) + for i := range expected { + assertUser(t, expected[i], actual[i]) + } +} + +func assertUser(t *testing.T, expected, actual *domain.User) { + t.Helper() + assert.Equal(t, expected.InstanceID, actual.InstanceID) + assert.Equal(t, expected.OrganizationID, actual.OrganizationID) + assert.Equal(t, expected.ID, actual.ID) + assert.Equal(t, expected.Username, actual.Username) + assert.Equal(t, expected.State, actual.State) + assert.True(t, expected.CreatedAt.Equal(actual.CreatedAt), "created at") + assert.True(t, expected.UpdatedAt.Equal(actual.UpdatedAt), "updated at") + + require.Equal(t, expected.Machine == nil, actual.Machine == nil, "machine check") + require.Equal(t, expected.Human == nil, actual.Human == nil, "human check") + + assertMetadata(t, expected.Metadata, actual.Metadata) + + if expected.Machine != nil { + assertMachineUser(t, expected.Machine, actual.Machine) + return + } + + if expected.Human != nil { + assertHumanUser(t, expected.Human, actual.Human) + return + } + t.Log("either machine or human must be set") + t.Fail() +} + +func assertMachineUser(t *testing.T, expected, actual *domain.MachineUser) { + t.Helper() + + assert.Equal(t, expected.Name, actual.Name, "machine name") + assert.Equal(t, expected.Description, actual.Description, "machine description") + assert.Equal(t, expected.AccessTokenType, actual.AccessTokenType, "machine access token type") + assert.Equal(t, expected.Secret, actual.Secret, "machine secret") + + assertPersonalAccessTokens(t, expected.PATs, actual.PATs) + assertMachineKeys(t, expected.Keys, actual.Keys) +} + +func assertHumanUser(t *testing.T, expected, actual *domain.HumanUser) { + t.Helper() + + assert.Equal(t, expected.FirstName, actual.FirstName, "human first name") + assert.Equal(t, expected.LastName, actual.LastName, "human last name") + assert.Equal(t, expected.Nickname, actual.Nickname, "human nickname") + assert.Equal(t, expected.DisplayName, actual.DisplayName, "human display name") + assert.Equal(t, expected.PreferredLanguage, actual.PreferredLanguage, "human preferred language") + assert.Equal(t, expected.Gender, actual.Gender, "human gender") + assert.Equal(t, expected.AvatarKey, actual.AvatarKey, "human avatar key") + assert.True(t, expected.MultifactorInitializationSkippedAt.Equal(actual.MultifactorInitializationSkippedAt), "human multifactor initialization skipped at") + + assertHumanEmail(t, expected.Email, actual.Email) + assertHumanPhone(t, expected.Phone, actual.Phone) + assertPasskeys(t, expected.Passkeys, actual.Passkeys) + assertHumanPassword(t, expected.Password, actual.Password) + assertHumanTOTP(t, expected.TOTP, actual.TOTP) + + assertIdentityProviderLinks(t, expected.IdentityProviderLinks, actual.IdentityProviderLinks) + assertVerifications(t, expected.Verifications, actual.Verifications) +} + +func assertHumanTOTP(t *testing.T, expected, actual *domain.HumanTOTP) { + t.Helper() + + require.Equal(t, expected == nil, actual == nil, "human totp nil check") + if expected == nil { + return + } + + assert.True(t, expected.VerifiedAt.Equal(actual.VerifiedAt), "human totp verified at") + assert.Equal(t, expected.Secret, actual.Secret) + if expected.LastSuccessfullyCheckedAt != nil { + require.NotNil(t, actual.LastSuccessfullyCheckedAt, "human totp last successfully checked at not nil") + assert.True(t, expected.LastSuccessfullyCheckedAt.Equal(*actual.LastSuccessfullyCheckedAt), "human totp last successfully checked at") + } else { + assert.Nil(t, actual.LastSuccessfullyCheckedAt, "human totp last successfully checked at nil") + } + assert.Equal(t, expected.FailedAttempts, actual.FailedAttempts, "human totp failed attempts") +} + +func assertHumanPassword(t *testing.T, expected, actual domain.HumanPassword) { + t.Helper() + + assert.Equal(t, expected.Hash, actual.Hash, "human password hash") + assert.Equal(t, expected.IsChangeRequired, actual.IsChangeRequired, "human password is change required") + assert.True(t, expected.ChangedAt.Equal(actual.ChangedAt), "human password changed at") + assertVerification(t, expected.PendingVerification, actual.PendingVerification, "human password pending verification") + if expected.LastSuccessfullyCheckedAt != nil { + require.NotNil(t, actual.LastSuccessfullyCheckedAt, "human password last successfully checked at not nil") + assert.True(t, expected.LastSuccessfullyCheckedAt.Equal(*actual.LastSuccessfullyCheckedAt), "human password last successfully checked at") + } else { + assert.Nil(t, actual.LastSuccessfullyCheckedAt, "human password last successfully checked at nil") + } + assert.Equal(t, expected.FailedAttempts, actual.FailedAttempts, "human password failed attempts") +} + +func assertHumanPhone(t *testing.T, expected, actual *domain.HumanPhone) { + t.Helper() + + require.Equal(t, expected == nil, actual == nil, "human phone number nil check") + if expected == nil { + return + } + + assert.Equal(t, expected.Number, actual.Number, "human phone number") + assert.Equal(t, expected.UnverifiedNumber, actual.UnverifiedNumber, "human unverified phone number") + assert.True(t, expected.VerifiedAt.Equal(actual.VerifiedAt), "human phone verified at") + assertOTP(t, expected.OTP, actual.OTP, "human phone otp") + assertVerification(t, expected.PendingVerification, actual.PendingVerification, "human phone pending verification") +} + +func assertHumanEmail(t *testing.T, expected, actual domain.HumanEmail) { + t.Helper() + + assert.Equal(t, expected.Address, actual.Address, "human email address") + assert.Equal(t, expected.UnverifiedAddress, actual.UnverifiedAddress, "human unverified email address") + assert.True(t, expected.VerifiedAt.Equal(actual.VerifiedAt), "human email verified at") + assertOTP(t, expected.OTP, actual.OTP, "human email otp") + assertVerification(t, expected.PendingVerification, actual.PendingVerification, "human email pending verification") +} + +func assertOTP(t *testing.T, expected, actual domain.OTP, field string) { + t.Helper() + + assert.True(t, expected.EnabledAt.Equal(actual.EnabledAt), field+" enabled at") + if expected.LastSuccessfullyCheckedAt != nil { + require.NotNil(t, actual.LastSuccessfullyCheckedAt, field+" last successfully checked at not nil") + assert.True(t, expected.LastSuccessfullyCheckedAt.Equal(*actual.LastSuccessfullyCheckedAt), field+" last successfully checked at") + } else { + assert.Nil(t, actual.LastSuccessfullyCheckedAt, field+" last successfully checked at nil") + } + assert.Equal(t, expected.FailedAttempts, actual.FailedAttempts, field+" failed attempts") +} + +func assertCryptoValue(t *testing.T, expected, actual *crypto.CryptoValue, field string) { + t.Helper() + + if expected == nil { + assert.Nil(t, actual, field+" nil") + return + } + + require.NotNil(t, actual, field+" not nil") + assert.Equal(t, expected.Crypted, actual.Crypted, field+" crypted") + assert.Equal(t, expected.CryptoType, actual.CryptoType, field+" crypto type") + assert.Equal(t, expected.Algorithm, actual.Algorithm, field+" algorithm") + assert.Equal(t, expected.KeyID, actual.KeyID, field+" key id") +} + +func assertPersonalAccessTokens(t *testing.T, expected, actual []*domain.PersonalAccessToken) { + assert.Len(t, actual, len(expected), "machine pats") + for _, expectedPAT := range expected { + var actualPAT *domain.PersonalAccessToken + actual = slices.DeleteFunc(actual, func(p *domain.PersonalAccessToken) bool { + if p.ID == expectedPAT.ID { + actualPAT = p + return true + } + return false + }) + require.NotNil(t, actualPAT, "pat[%s] not found", expectedPAT.ID) + assert.Equal(t, expectedPAT.ID, actualPAT.ID, "pat[%s] id", expectedPAT.ID) + assert.True(t, expectedPAT.CreatedAt.Equal(actualPAT.CreatedAt), "pat[%s] created at", expectedPAT.ID) + assert.True(t, expectedPAT.ExpiresAt.Equal(actualPAT.ExpiresAt), "pat[%s] expires at", expectedPAT.ID) + assert.ElementsMatch(t, expectedPAT.Scopes, actualPAT.Scopes, "pat[%s] scopes", expectedPAT.ID) + } + assert.Empty(t, actual, "unmatched machine pats") +} + +func assertMachineKeys(t *testing.T, expected, actual []*domain.MachineKey) { + t.Helper() + + require.Len(t, actual, len(expected), "machine keys") + for _, expectedKey := range expected { + var actualKey *domain.MachineKey + actual = slices.DeleteFunc(actual, func(k *domain.MachineKey) bool { + if k.ID == expectedKey.ID { + actualKey = k + return true + } + return false + }) + require.NotNil(t, actualKey, "machine key[%s] not found", expectedKey.ID) + assert.Equal(t, expectedKey.PublicKey, actualKey.PublicKey, "machine key[%s] public key", expectedKey.ID) + assert.True(t, expectedKey.CreatedAt.Equal(actualKey.CreatedAt), "machine key[%s] created at", expectedKey.ID) + assert.True(t, expectedKey.ExpiresAt.Equal(actualKey.ExpiresAt), "machine key[%s] expires at", expectedKey.ID) + assert.Equal(t, expectedKey.Type, actualKey.Type, "machine key[%s] type", expectedKey.ID) + } + assert.Empty(t, actual, "unmatched machine keys") +} + +func assertPasskeys(t *testing.T, expected, actual []*domain.Passkey) { + t.Helper() + + for i, expectedPasskey := range expected { + var actualPasskey *domain.Passkey + actual = slices.DeleteFunc(actual, func(p *domain.Passkey) bool { + if p.ID == expectedPasskey.ID { + actualPasskey = p + return true + } + return false + }) + require.NotNil(t, actualPasskey, "human passkey[%d] not found", i) + assert.Equal(t, expectedPasskey.KeyID, actualPasskey.KeyID, "human passkey[%d] key id", i) + assert.Equal(t, expectedPasskey.Name, actualPasskey.Name, "human passkey[%d] name", i) + assert.Equal(t, expectedPasskey.SignCount, actualPasskey.SignCount, "human passkey[%d] sign count", i) + assert.Equal(t, expectedPasskey.PublicKey, actualPasskey.PublicKey, "human passkey[%d] public key", i) + assert.Equal(t, expectedPasskey.AttestationType, actualPasskey.AttestationType, "human passkey[%d] attestation type", i) + assert.Equal(t, expectedPasskey.AuthenticatorAttestationGUID, actualPasskey.AuthenticatorAttestationGUID, "human passkey[%d] authenticator attestation guid", i) + assert.Equal(t, expectedPasskey.Type, actualPasskey.Type, "human passkey[%d] type", i) + assert.True(t, expectedPasskey.CreatedAt.Equal(actualPasskey.CreatedAt), "human passkey[%d] created at", i) + assert.True(t, expectedPasskey.UpdatedAt.Equal(actualPasskey.UpdatedAt), "human passkey[%d] updated at", i) + assert.True(t, expectedPasskey.VerifiedAt.Equal(actualPasskey.VerifiedAt), "human passkey[%d] verified at", i) + assert.Equal(t, expectedPasskey.Challenge, actualPasskey.Challenge, "human passkey[%d] challenge", i) + assert.Equal(t, expectedPasskey.RelyingPartyID, actualPasskey.RelyingPartyID, "human passkey[%d] relying party id", i) + assertVerification(t, expectedPasskey.Initialization, actualPasskey.Initialization, fmt.Sprintf("human passkey[%d] initialization", i)) + } + assert.Empty(t, actual, "unmatched human passkeys") +} + +func assertVerifications(t *testing.T, expected, actual []*domain.Verification) { + t.Helper() + + for i, expectedVerification := range expected { + var actualVerification *domain.Verification + actual = slices.DeleteFunc(actual, func(v *domain.Verification) bool { + if v.ID == expectedVerification.ID { + actualVerification = v + return true + } + return false + }) + require.NotNil(t, actualVerification, "human verification[%d] not found", i) + assert.Equal(t, expectedVerification.ID, actualVerification.ID, "human verification[%d] id", i) + assert.Equal(t, expectedVerification.Code, actualVerification.Code, "human verification[%d] code", i) + if expectedVerification.ExpiresAt != nil { + require.NotNil(t, actualVerification.ExpiresAt, "human verification[%d] expires at not nil", i) + assert.True(t, expectedVerification.ExpiresAt.Equal(*actualVerification.ExpiresAt), "human verification[%d] expires at", i) + } else { + assert.Nil(t, actualVerification.ExpiresAt, "human verification[%d] expires at nil", i) + } + assert.Equal(t, expectedVerification.FailedAttempts, actualVerification.FailedAttempts, "human verification[%d] failed attempts", i) + } + assert.Empty(t, actual, "unmatched human verifications") +} + +func assertVerification(t *testing.T, expected, actual *domain.Verification, field string) { + t.Helper() + + require.Equal(t, expected == nil, actual == nil, "%s nil check", field) + if expected == nil { + return + } + + assert.Equal(t, expected.ID, actual.ID, "%s verification id", field+" id") + assertCryptoValue(t, expected.Code, actual.Code, field+" code") + assert.True(t, expected.CreatedAt.Equal(actual.CreatedAt), field+" created at") + assert.True(t, expected.UpdatedAt.Equal(actual.UpdatedAt), field+" updated at") + + if expected.ExpiresAt != nil { + require.NotNil(t, actual.ExpiresAt, "%s expires at not nil", field) + assert.True(t, expected.ExpiresAt.Equal(*actual.ExpiresAt), "%s expires at", field) + } else { + assert.Nil(t, actual.ExpiresAt, "%s expires at nil", field) + } + assert.Equal(t, expected.FailedAttempts, actual.FailedAttempts, "%s failed attempts", field) +} + +func assertIdentityProviderLinks(t *testing.T, expected, actual []*domain.IdentityProviderLink) { + t.Helper() + + require.Len(t, actual, len(expected), "identity provider links") + for i, expectedLink := range expected { + assert.Equal(t, expectedLink.ProviderID, actual[i].ProviderID) + assert.Equal(t, expectedLink.ProvidedUserID, actual[i].ProvidedUserID) + assert.Equal(t, expectedLink.ProvidedUsername, actual[i].ProvidedUsername) + assert.True(t, expectedLink.CreatedAt.Equal(actual[i].CreatedAt), "identity provider link[%d] created at", i) + assert.True(t, expectedLink.UpdatedAt.Equal(actual[i].UpdatedAt), "identity provider link[%d] updated at", i) + } +} diff --git a/backend/v3/storage/database/repository/verification.go b/backend/v3/storage/database/repository/verification.go new file mode 100644 index 000000000..981f29e88 --- /dev/null +++ b/backend/v3/storage/database/repository/verification.go @@ -0,0 +1,171 @@ +package repository + +import ( + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/instrumentation/logging" + "github.com/zitadel/zitadel/backend/v3/storage/database" +) + +type verification struct{} + +func (v verification) unqualifiedTableName() string { + return "verifications" +} + +func (v verification) qualifiedTableName() string { + return "zitadel." + v.unqualifiedTableName() +} + +// init creates a new verification +func (v verification) init(init *domain.VerificationTypeInit, existingTableName string, verificationID database.Column) database.CTEChange { + return database.NewCTEChange(func(builder *database.StatementBuilder) { + var ( + createdAt any = database.NowInstruction + expiry any = database.NullInstruction + id any = database.GenRandomUUIDInstruction + ) + if !init.CreatedAt.IsZero() { + createdAt = init.CreatedAt + } + if init.Expiry != nil { + expiry = *init.Expiry + } + if init.ID != nil { + id = *init.ID + } + builder.WriteString("INSERT INTO zitadel.verifications(instance_id, user_id, id, code, created_at, updated_at, expiry) SELECT instance_id, id, ") + builder.WriteArgs(id, init.Code, createdAt, createdAt, expiry) + builder.WriteString(" FROM ") + builder.WriteString(existingTableName) + builder.WriteString(" RETURNING id") + }, func(name string) database.Change { + return database.NewChangeToStatement(verificationID, func(builder *database.StatementBuilder) { + builder.WriteString("SELECT id FROM ") + builder.WriteString(name) + }) + }) +} + +// updates the given fields of the verification with the given ID. Only non-nil fields get updated. +func (v verification) update(update *domain.VerificationTypeUpdate, existingTableName string, instanceID, verificationID database.Column) database.CTEChange { + changes := make(database.Changes, 0, 3) + if update.Code != nil { + changes = append(changes, database.NewChange(v.codeColumn(), update.Code)) + } + if update.Expiry != nil { + changes = append(changes, database.NewChange(v.expiryColumn(), *update.Expiry)) + } + if update.UpdatedAt.IsZero() { + changes = append(changes, database.NewChange(v.updatedAtColumn(), database.NowInstruction)) + } else { + changes = append(changes, database.NewChange(v.updatedAtColumn(), update.UpdatedAt)) + } + return database.NewCTEChange(func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE ") + builder.WriteString(v.qualifiedTableName()) + builder.WriteString(" SET ") + err := changes.Write(builder) + logging.New(logging.StreamRuntime).Debug("write changes in cte failed", "error", err) + builder.WriteString(" FROM ") + builder.WriteString(existingTableName) + writeCondition(builder, database.And( + database.NewColumnCondition(v.instanceIDColumn(), instanceID), + database.NewColumnCondition(v.idColumn(), verificationID), + )) + }, nil) +} + +// verified constructs the following changes: +// - sets the verifiedAt column to the current time or the provided time +// - clears the pending verification ID +// - deletes the verification +// - resets the failed attempts counter +func (v verification) verified(verified *domain.VerificationTypeSucceeded, existingTableName string, instanceID, pendingVerificationID, verifiedAt, failedAttempts database.Column) database.Changes { + verifiedAtChange := database.NewChange(verifiedAt, database.NowInstruction) + if !verified.VerifiedAt.IsZero() { + verifiedAtChange = database.NewChange(verifiedAt, verified.VerifiedAt) + } + + return database.NewChanges( + verifiedAtChange, + database.NewChangeToNull(pendingVerificationID), + database.NewChange(failedAttempts, 0), + database.NewCTEChange( + func(builder *database.StatementBuilder) { + builder.WriteString("DELETE FROM ") + builder.WriteString(v.qualifiedTableName()) + builder.WriteString(" USING ") + builder.WriteString(existingTableName) + writeCondition(builder, database.And( + database.NewColumnCondition(v.instanceIDColumn(), instanceID), + database.NewColumnCondition(v.idColumn(), pendingVerificationID), + )) + }, nil, + ), + ) +} + +// skipped constructs the following changes: +// - sets the verifiedAt column to the current time or the provided time +// - clears the pending verification ID +// - resets the failed attempts counter +func (v verification) skipped(skipped *domain.VerificationTypeSkipped, verifiedAt, pendingVerificationID, failedAttempts database.Column) database.Changes { + verifiedAtChange := database.NewChange(verifiedAt, database.NowInstruction) + if !skipped.SkippedAt.IsZero() { + verifiedAtChange = database.NewChange(verifiedAt, skipped.SkippedAt) + } + return database.NewChanges( + verifiedAtChange, + database.NewChange(failedAttempts, 0), + database.NewChangeToNull(pendingVerificationID), + ) +} + +// failed increments the failed attempts counter. +func (v verification) failed(existingTableName string, instanceID, verificationID database.Column) database.CTEChange { + return database.NewCTEChange(func(builder *database.StatementBuilder) { + builder.WriteString("UPDATE ") + builder.WriteString(v.qualifiedTableName()) + builder.WriteString(" SET ") + err := database.NewIncrementColumnChange(v.failedAttemptsColumn(), database.Coalesce(v.failedAttemptsColumn(), 0)).Write(builder) + logging.New(logging.StreamRuntime).Debug("write cte failed", "error", err) + builder.WriteString(" FROM ") + builder.WriteString(existingTableName) + writeCondition(builder, database.And( + database.NewColumnCondition(v.instanceIDColumn(), instanceID), + database.NewColumnCondition(v.idColumn(), verificationID), + )) + }, nil) +} + +// ------------------------------------------------------------- +// columns +// ------------------------------------------------------------- + +func (v verification) instanceIDColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "instance_id") +} + +func (v verification) idColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "id") +} + +func (v verification) codeColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "code") +} + +func (v verification) expiryColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "expiry") +} + +func (v verification) failedAttemptsColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "failed_attempts") +} + +func (v verification) userIDColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "user_id") +} + +func (v verification) updatedAtColumn() database.Column { + return database.NewColumn(v.unqualifiedTableName(), "updated_at") +} diff --git a/backend/v3/storage/database/statement.go b/backend/v3/storage/database/statement.go index 4b01f60fe..b7bfe7cf7 100644 --- a/backend/v3/storage/database/statement.go +++ b/backend/v3/storage/database/statement.go @@ -8,9 +8,10 @@ import ( type Instruction string const ( - DefaultInstruction Instruction = "DEFAULT" - NowInstruction Instruction = "NOW()" - NullInstruction Instruction = "NULL" + DefaultInstruction Instruction = "DEFAULT" + NowInstruction Instruction = "NOW()" + NullInstruction Instruction = "NULL" + GenRandomUUIDInstruction Instruction = "gen_random_uuid()" ) // StatementBuilder is a helper to build SQL statement. diff --git a/go.mod b/go.mod index 913c0f0cf..77a54a3f0 100644 --- a/go.mod +++ b/go.mod @@ -30,12 +30,13 @@ require ( github.com/drone/envsubst v1.0.3 github.com/envoyproxy/protoc-gen-validate v1.2.1 github.com/fatih/color v1.18.0 - github.com/fergusstrange/embedded-postgres v1.30.0 + github.com/fergusstrange/embedded-postgres v1.33.0 github.com/gabriel-vasile/mimetype v1.4.11 github.com/georgysavva/scany/v2 v2.1.4 github.com/go-chi/chi/v5 v5.2.3 github.com/go-jose/go-jose/v4 v4.1.3 github.com/go-ldap/ldap/v3 v3.4.12 + github.com/go-resty/resty/v2 v2.16.5 github.com/go-webauthn/webauthn v0.10.2 github.com/goccy/go-json v0.10.5 github.com/golang/protobuf v1.5.4 @@ -134,6 +135,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 google.golang.org/grpc v1.77.0 google.golang.org/protobuf v1.36.10 + gopkg.in/headzoo/surf.v1 v1.0.1 sigs.k8s.io/yaml v1.6.0 ) @@ -152,7 +154,9 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/PuerkitoBio/goquery v1.5.1 // indirect github.com/amdonov/xmlsig v0.1.0 // indirect + github.com/andybalholm/cascadia v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -199,6 +203,8 @@ require ( github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect + github.com/headzoo/surf v1.0.1 // indirect + github.com/headzoo/ut v0.0.0-20181013193318-a13b5a7a02ca // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/go.sum b/go.sum index b5f104951..e83684c65 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,7 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -86,6 +87,7 @@ github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21j github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/amdonov/xmlsig v0.1.0 h1:i0iQ3neKLmUhcfIRgiiR3eRPKgXZj+n5lAfqnfKoeXI= github.com/amdonov/xmlsig v0.1.0/go.mod h1:jTR/jO0E8fSl/cLvMesP+RjxyV4Ux4WL1Ip64ZnQpA0= +github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -224,8 +226,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fergusstrange/embedded-postgres v1.30.0 h1:ewv1e6bBlqOIYtgGgRcEnNDpfGlmfPxB8T3PO9tV68Q= -github.com/fergusstrange/embedded-postgres v1.30.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk= +github.com/fergusstrange/embedded-postgres v1.33.0 h1:ka8vmRpm4IDsES7NPXQ/NThAp1fc/f+crcXYjCW7wK0= +github.com/fergusstrange/embedded-postgres v1.33.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -274,6 +276,8 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= +github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -429,6 +433,10 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/headzoo/surf v1.0.1 h1:wk3+LT8gjnCxEwfBJl6MhaNg154En5KjgmgzAG9uMS0= +github.com/headzoo/surf v1.0.1/go.mod h1:/bct0m/iMNEqpn520y01yoaWxsAEigGFPnvyR1ewR5M= +github.com/headzoo/ut v0.0.0-20181013193318-a13b5a7a02ca h1:utFgFwgxaqx5OthzE3DSGrtOq7rox5r2sxZ2wbfTuK0= +github.com/headzoo/ut v0.0.0-20181013193318-a13b5a7a02ca/go.mod h1:8926sG02TCOX4RFRzIMFIzRw4xuc/TwO2gtN7teMJZ4= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -1128,6 +1136,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/headzoo/surf.v1 v1.0.1 h1:oDBy9b5NlTb2Hvl3hF8NN+Qy7ypC9/g5YDP85pPh13k= +gopkg.in/headzoo/surf.v1 v1.0.1/go.mod h1:T0BH8276y+OPL0E4tisxCFjBVIAKGbwdYU7AS7/EpQQ= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/internal/database/postgres/embedded.go b/internal/database/postgres/embedded.go index 57aec756f..1043c63dc 100644 --- a/internal/database/postgres/embedded.go +++ b/internal/database/postgres/embedded.go @@ -14,7 +14,7 @@ func StartEmbedded() (embeddedpostgres.Config, func()) { port, close := getPort() - config := embeddedpostgres.DefaultConfig().Version(embeddedpostgres.V16).Port(uint32(port)).RuntimePath(path) + config := embeddedpostgres.DefaultConfig().Version(embeddedpostgres.V17).Port(uint32(port)).RuntimePath(path) embedded := embeddedpostgres.NewDatabase(config) close() diff --git a/internal/query/projection/idp_intent_relational.go b/internal/query/projection/idp_intent_relational.go index 9009a9172..d9fbf6558 100644 --- a/internal/query/projection/idp_intent_relational.go +++ b/internal/query/projection/idp_intent_relational.go @@ -1,257 +1,255 @@ package projection -// TODO(adlerhurst): Uncomment once users table is created and FK can be enforced +import ( + "context" + "database/sql" + "encoding/json" -// import ( -// "context" -// "database/sql" -// "encoding/json" + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/repository/idpintent" + "github.com/zitadel/zitadel/internal/zerrors" +) -// "github.com/zitadel/zitadel/backend/v3/domain" -// "github.com/zitadel/zitadel/backend/v3/storage/database" -// v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" -// "github.com/zitadel/zitadel/backend/v3/storage/database/repository" -// "github.com/zitadel/zitadel/internal/eventstore" -// "github.com/zitadel/zitadel/internal/eventstore/handler/v2" -// "github.com/zitadel/zitadel/internal/repository/idpintent" -// "github.com/zitadel/zitadel/internal/zerrors" -// ) +const ( + IDPIntentRelationalProjectionTable = "zitadel.identity_provider_intents" +) -// const ( -// IDPIntentRelationalProjectionTable = "zitadel.identity_provider_intents" -// ) +type idpIntentRelationalProjection struct{} -// type idpIntentRelationalProjection struct{} +func (*idpIntentRelationalProjection) Name() string { + return IDPIntentRelationalProjectionTable +} -// func (*idpIntentRelationalProjection) Name() string { -// return IDPIntentRelationalProjectionTable -// } +func newIDPIntentRelationalProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(idpIntentRelationalProjection)) +} -// func newIDPIntentRelationalProjection(ctx context.Context, config handler.Config) *handler.Handler { -// return handler.NewHandler(ctx, &config, new(idpIntentRelationalProjection)) -// } +// Reducers implements [handler.Projection]. +func (i *idpIntentRelationalProjection) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: idpintent.AggregateType, + EventReducers: []handler.EventReducer{ + {Event: idpintent.StartedEventType, Reduce: i.reduceStartedEvent}, + {Event: idpintent.SucceededEventType, Reduce: i.reduceSucceededEvent}, + {Event: idpintent.SAMLSucceededEventType, Reduce: i.reduceSAMLSucceededEvent}, + {Event: idpintent.SAMLRequestEventType, Reduce: i.reduceSAMLRequestEvent}, + {Event: idpintent.LDAPSucceededEventType, Reduce: i.reduceLDAPSucceededEvent}, + {Event: idpintent.FailedEventType, Reduce: i.reduceFailedEvent}, + {Event: idpintent.ConsumedEventType, Reduce: i.reduceConsumedEvent}, + }, + }, + } -// // Reducers implements [handler.Projection]. -// func (i *idpIntentRelationalProjection) Reducers() []handler.AggregateReducer { -// return []handler.AggregateReducer{ -// { -// Aggregate: idpintent.AggregateType, -// EventReducers: []handler.EventReducer{ -// {Event: idpintent.StartedEventType, Reduce: i.reduceStartedEvent}, -// {Event: idpintent.SucceededEventType, Reduce: i.reduceSucceededEvent}, -// {Event: idpintent.SAMLSucceededEventType, Reduce: i.reduceSAMLSucceededEvent}, -// {Event: idpintent.SAMLRequestEventType, Reduce: i.reduceSAMLRequestEvent}, -// {Event: idpintent.LDAPSucceededEventType, Reduce: i.reduceLDAPSucceededEvent}, -// {Event: idpintent.FailedEventType, Reduce: i.reduceFailedEvent}, -// {Event: idpintent.ConsumedEventType, Reduce: i.reduceConsumedEvent}, -// }, -// }, -// } +} -// } +func (i *idpIntentRelationalProjection) reduceStartedEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.StartedEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-Lqj3HB", "reduce.wrong.event.type %s", idpintent.StartedEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-rirFQm", "reduce.wrong.db.pool %T", ex) + } -// func (i *idpIntentRelationalProjection) reduceStartedEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.StartedEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-Lqj3HB", "reduce.wrong.event.type %s", idpintent.StartedEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-rirFQm", "reduce.wrong.db.pool %T", ex) -// } + repo := repository.IDPIntentRepository() + return repo.Create(ctx, v3_sql.SQLTx(tx), &domain.IDPIntent{ + ID: e.Aggregate().ID, + InstanceID: e.Aggregate().InstanceID, + State: domain.IDPIntentStateStarted, + SuccessURL: e.SuccessURL, + FailureURL: e.FailureURL, + CreatedAt: e.CreationDate(), + UpdatedAt: e.CreationDate(), + IDPID: e.IDPID, + IDPArguments: e.IDPArguments, + }) + }), nil +} -// repo := repository.IDPIntentRepository() -// return repo.Create(ctx, v3_sql.SQLTx(tx), &domain.IDPIntent{ -// ID: e.Aggregate().ID, -// InstanceID: e.Aggregate().InstanceID, -// State: domain.IDPIntentStateStarted, -// SuccessURL: e.SuccessURL, -// FailureURL: e.FailureURL, -// CreatedAt: e.CreationDate(), -// UpdatedAt: e.CreationDate(), -// IDPID: e.IDPID, -// IDPArguments: e.IDPArguments, -// }) -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.SucceededEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-hQSxj0", "reduce.wrong.event.type %s", idpintent.SucceededEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-ry4tFH", "reduce.wrong.db.pool %T", ex) + } + idpAccessToken, err := e.IDPAccessToken.Value() + if err != nil { + return err + } + jsonIDPAccessToken, ok := idpAccessToken.([]byte) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-32XE1L", "reduce.convertion.json.failed") + } + repo := repository.IDPIntentRepository() -// func (i *idpIntentRelationalProjection) reduceSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.SucceededEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-hQSxj0", "reduce.wrong.event.type %s", idpintent.SucceededEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-ry4tFH", "reduce.wrong.db.pool %T", ex) -// } -// idpAccessToken, err := e.IDPAccessToken.Value() -// if err != nil { -// return err -// } -// jsonIDPAccessToken, ok := idpAccessToken.([]byte) -// if !ok { -// return zerrors.ThrowInternal(nil, "HANDL-32XE1L", "reduce.convertion.json.failed") -// } -// repo := repository.IDPIntentRepository() + changes := []database.Change{ + repo.SetState(domain.IDPIntentStateSucceeded), + repo.SetIDPUser(e.IDPUser), + repo.SetIDPUserID(e.IDPUserID), + repo.SetIDPUsername(e.IDPUserName), + repo.SetUserID(e.UserID), + repo.SetIDPAccessToken(jsonIDPAccessToken), + repo.SetIDPIDToken(e.IDPIDToken), + repo.SetSucceededAt(e.CreationDate()), + repo.SetExpiresAt(e.ExpiresAt), + } -// changes := []database.Change{ -// repo.SetState(domain.IDPIntentStateSucceeded), -// repo.SetIDPUser(e.IDPUser), -// repo.SetIDPUserID(e.IDPUserID), -// repo.SetIDPUsername(e.IDPUserName), -// repo.SetUserID(e.UserID), -// repo.SetIDPAccessToken(jsonIDPAccessToken), -// repo.SetIDPIDToken(e.IDPIDToken), -// repo.SetSucceededAt(e.CreationDate()), -// repo.SetExpiresAt(e.ExpiresAt), -// } + _, err = repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + changes..., + ) + return err + }), nil +} -// _, err = repo.Update( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// changes..., -// ) -// return err -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceSAMLSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.SAMLSucceededEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-t2uaiv", "reduce.wrong.event.type %s", idpintent.SAMLSucceededEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-0QGPXm", "reduce.wrong.db.pool %T", ex) + } + assertion, err := e.Assertion.Value() + if err != nil { + return err + } + jsonAssertion, ok := assertion.([]byte) + if !ok { + return zerrors.ThrowInternal(nil, "HANDL-1TFjTi", "reduce.convertion.json.failed") + } -// func (i *idpIntentRelationalProjection) reduceSAMLSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.SAMLSucceededEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-t2uaiv", "reduce.wrong.event.type %s", idpintent.SAMLSucceededEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-0QGPXm", "reduce.wrong.db.pool %T", ex) -// } -// assertion, err := e.Assertion.Value() -// if err != nil { -// return err -// } -// jsonAssertion, ok := assertion.([]byte) -// if !ok { -// return zerrors.ThrowInternal(nil, "HANDL-1TFjTi", "reduce.convertion.json.failed") -// } + repo := repository.IDPIntentRepository() + _, err = repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetState(domain.IDPIntentStateSucceeded), + repo.SetIDPUser(e.IDPUser), + repo.SetIDPUserID(e.IDPUserID), + repo.SetIDPUsername(e.IDPUserName), + repo.SetUserID(e.UserID), + repo.SetAssertion(jsonAssertion), + repo.SetSucceededAt(e.CreationDate()), + repo.SetExpiresAt(e.ExpiresAt), + ) + return err + }), nil +} -// repo := repository.IDPIntentRepository() -// _, err = repo.Update( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// repo.SetState(domain.IDPIntentStateSucceeded), -// repo.SetIDPUser(e.IDPUser), -// repo.SetIDPUserID(e.IDPUserID), -// repo.SetIDPUsername(e.IDPUserName), -// repo.SetUserID(e.UserID), -// repo.SetAssertion(jsonAssertion), -// repo.SetSucceededAt(e.CreationDate()), -// repo.SetExpiresAt(e.ExpiresAt), -// ) -// return err -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceSAMLRequestEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.SAMLRequestEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-f7O6K2", "reduce.wrong.event.type %s", idpintent.SAMLRequestEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-ijgAPJ", "reduce.wrong.db.pool %T", ex) + } -// func (i *idpIntentRelationalProjection) reduceSAMLRequestEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.SAMLRequestEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-f7O6K2", "reduce.wrong.event.type %s", idpintent.SAMLRequestEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-ijgAPJ", "reduce.wrong.db.pool %T", ex) -// } + repo := repository.IDPIntentRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetRequestID(e.RequestID), + ) + return err + }), nil +} -// repo := repository.IDPIntentRepository() -// _, err := repo.Update( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// repo.SetRequestID(e.RequestID), -// ) -// return err -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceLDAPSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.LDAPSucceededEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-Gw8ddW", "reduce.wrong.event.type %s", idpintent.LDAPSucceededEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-tNp8aa", "reduce.wrong.db.pool %T", ex) + } -// func (i *idpIntentRelationalProjection) reduceLDAPSucceededEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.LDAPSucceededEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-Gw8ddW", "reduce.wrong.event.type %s", idpintent.LDAPSucceededEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-tNp8aa", "reduce.wrong.db.pool %T", ex) -// } + jsonAttributes, err := json.Marshal(e.EntryAttributes) + if err != nil { + return zerrors.ThrowInternal(nil, "HANDL-LuvCvD", "reduce.convertion.json.failed") + } -// jsonAttributes, err := json.Marshal(e.EntryAttributes) -// if err != nil { -// return zerrors.ThrowInternal(nil, "HANDL-LuvCvD", "reduce.convertion.json.failed") -// } + repo := repository.IDPIntentRepository() + _, err = repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetState(domain.IDPIntentStateSucceeded), + repo.SetIDPUser(e.IDPUser), + repo.SetIDPUserID(e.IDPUserID), + repo.SetIDPUsername(e.IDPUserName), + repo.SetUserID(e.UserID), + repo.SetIDPEntryAttributes(jsonAttributes), + repo.SetSucceededAt(e.CreationDate()), + repo.SetExpiresAt(e.ExpiresAt), + ) + return err + }), nil +} -// repo := repository.IDPIntentRepository() -// _, err = repo.Update( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// repo.SetState(domain.IDPIntentStateSucceeded), -// repo.SetIDPUser(e.IDPUser), -// repo.SetIDPUserID(e.IDPUserID), -// repo.SetIDPUsername(e.IDPUserName), -// repo.SetUserID(e.UserID), -// repo.SetIDPEntryAttributes(jsonAttributes), -// repo.SetSucceededAt(e.CreationDate()), -// repo.SetExpiresAt(e.ExpiresAt), -// ) -// return err -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceFailedEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.FailedEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-x3o9LZ", "reduce.wrong.event.type %s", idpintent.FailedEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iqmR8O", "reduce.wrong.db.pool %T", ex) + } -// func (i *idpIntentRelationalProjection) reduceFailedEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.FailedEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-x3o9LZ", "reduce.wrong.event.type %s", idpintent.FailedEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iqmR8O", "reduce.wrong.db.pool %T", ex) -// } + repo := repository.IDPIntentRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetState(domain.IDPIntentStateFailed), + repo.SetFailReason(e.Reason), + repo.SetFailedAt(e.CreatedAt()), + ) + return err + }), nil +} -// repo := repository.IDPIntentRepository() -// _, err := repo.Update( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// repo.SetState(domain.IDPIntentStateFailed), -// repo.SetFailReason(e.Reason), -// repo.SetFailedAt(e.CreatedAt()), -// ) -// return err -// }), nil -// } +func (i *idpIntentRelationalProjection) reduceConsumedEvent(evt eventstore.Event) (*handler.Statement, error) { + e, ok := evt.(*idpintent.ConsumedEvent) + if !ok { + return nil, zerrors.ThrowInternalf(nil, "HANDL-jHZs6T", "reduce.wrong.event.type %s", idpintent.ConsumedEventType) + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-8X9U9w", "reduce.wrong.db.pool %T", ex) + } -// func (i *idpIntentRelationalProjection) reduceConsumedEvent(evt eventstore.Event) (*handler.Statement, error) { -// e, ok := evt.(*idpintent.ConsumedEvent) -// if !ok { -// return nil, zerrors.ThrowInternalf(nil, "HANDL-jHZs6T", "reduce.wrong.event.type %s", idpintent.ConsumedEventType) -// } -// return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, _ string) error { -// tx, ok := ex.(*sql.Tx) -// if !ok { -// return zerrors.ThrowInvalidArgumentf(nil, "HANDL-8X9U9w", "reduce.wrong.db.pool %T", ex) -// } - -// repo := repository.IDPIntentRepository() -// _, err := repo.Delete( -// ctx, -// v3_sql.SQLTx(tx), -// repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), -// ) -// return err -// }), nil -// } + repo := repository.IDPIntentRepository() + _, err := repo.Delete( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + ) + return err + }), nil +} diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index f5faf3c1e..227af82fb 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -101,8 +101,8 @@ var ( ProjectGrantRelationalProjection *handler.Handler SessionRelationalProjection *handler.Handler SettingsRelationalProjection *handler.Handler - // TODO(adlerhurst): Uncomment once users table is created and FK can be enforced - // IDPIntentRelationalProjection *handler.Handler + UserRelationalProjection *handler.Handler + IDPIntentRelationalProjection *handler.Handler ProjectGrantFields *handler.FieldHandler OrgDomainVerifiedFields *handler.FieldHandler @@ -232,8 +232,8 @@ func Create(ctx context.Context, sqlClient *database.DB, es handler.EventStore, AuthorizationRelationalProjection = newAuthorizationRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["authorizations_relational"])) ProjectGrantRelationalProjection = newProjectGrantRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["project_grant_relational"])) SessionRelationalProjection = newSessionRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["session_relational"])) - // TODO(adlerhurst): Uncomment once users table is created and FK can be enforced - // IDPIntentRelationalProjection = newIDPIntentRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idp_intent_relational"])) + UserRelationalProjection = newUserRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_relational"])) + IDPIntentRelationalProjection = newIDPIntentRelationalProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idp_intent_relational"])) newProjectionsList() newFieldsList() @@ -428,10 +428,10 @@ func newProjectionsList() { ProjectRelationalProjection, ProjectRoleRelationalProjection, OrganizationMetadataRelationalProjection, + UserRelationalProjection, AuthorizationRelationalProjection, ProjectGrantRelationalProjection, SessionRelationalProjection, - // TODO(adlerhurst): Uncomment once users table is created and FK can be enforced - // IDPIntentRelationalProjection, + IDPIntentRelationalProjection, } } diff --git a/internal/query/projection/user_relational.go b/internal/query/projection/user_relational.go new file mode 100644 index 000000000..eee4fb8af --- /dev/null +++ b/internal/query/projection/user_relational.go @@ -0,0 +1,2154 @@ +package projection + +import ( + "context" + "database/sql" + "time" + + "github.com/zitadel/zitadel/backend/v3/domain" + "github.com/zitadel/zitadel/backend/v3/storage/database" + v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" + "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/crypto" + old_domain "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/repository/user" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const ( + UserRelationProjectionTable = "zitadel.users" +) + +type userRelationalProjection struct{} + +func (*userRelationalProjection) Name() string { + return UserRelationProjectionTable +} + +func newUserRelationalProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(userRelationalProjection)) +} + +func (p *userRelationalProjection) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: user.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: user.UserV1AddedType, + Reduce: p.reduceHumanAdded, + }, + { + Event: user.HumanAddedType, + Reduce: p.reduceHumanAdded, + }, + { + Event: user.UserV1RegisteredType, + Reduce: p.reduceHumanRegistered, + }, + { + Event: user.HumanRegisteredType, + Reduce: p.reduceHumanRegistered, + }, + { + Event: user.UserLockedType, + Reduce: p.reduceUserLocked, + }, + { + Event: user.UserUnlockedType, + Reduce: p.reduceUserUnlocked, + }, + { + Event: user.UserDeactivatedType, + Reduce: p.reduceUserDeactivated, + }, + { + Event: user.UserReactivatedType, + Reduce: p.reduceUserReactivated, + }, + { + Event: user.UserRemovedType, + Reduce: p.reduceUserRemoved, + }, + + { + Event: user.UserUserNameChangedType, + Reduce: p.reduceUsernameChanged, + }, + { + Event: user.UserDomainClaimedType, + Reduce: p.reduceDomainClaimed, + }, + + { + Event: user.HumanProfileChangedType, + Reduce: p.reduceHumanProfileChanged, + }, + { + Event: user.UserV1ProfileChangedType, + Reduce: p.reduceHumanProfileChanged, + }, + + { + Event: user.HumanEmailChangedType, + Reduce: p.reduceHumanEmailChanged, + }, + { + Event: user.UserV1EmailChangedType, + Reduce: p.reduceHumanEmailChanged, + }, + { + Event: user.HumanEmailVerifiedType, + Reduce: p.reduceHumanEmailVerified, + }, + { + Event: user.UserV1EmailVerifiedType, + Reduce: p.reduceHumanEmailVerified, + }, + { + Event: user.HumanEmailCodeAddedType, + Reduce: p.reduceHumanEmailCodeAdded, + }, + { + Event: user.UserV1EmailCodeAddedType, + Reduce: p.reduceHumanEmailCodeAdded, + }, + { + Event: user.HumanEmailVerificationFailedType, + Reduce: p.reduceHumanEmailVerificationFailed, + }, + { + Event: user.UserV1EmailVerificationFailedType, + Reduce: p.reduceHumanEmailVerificationFailed, + }, + { + Event: user.HumanPhoneChangedType, + Reduce: p.reduceHumanPhoneChanged, + }, + { + Event: user.UserV1PhoneChangedType, + Reduce: p.reduceHumanPhoneChanged, + }, + { + Event: user.HumanPhoneRemovedType, + Reduce: p.reduceHumanPhoneRemoved, + }, + { + Event: user.UserV1PhoneRemovedType, + Reduce: p.reduceHumanPhoneRemoved, + }, + { + Event: user.HumanPhoneVerifiedType, + Reduce: p.reduceHumanPhoneVerified, + }, + { + Event: user.UserV1PhoneVerifiedType, + Reduce: p.reduceHumanPhoneVerified, + }, + { + Event: user.HumanPhoneCodeAddedType, + Reduce: p.reduceHumanPhoneCodeAdded, + }, + { + Event: user.UserV1PhoneCodeAddedType, + Reduce: p.reduceHumanPhoneCodeAdded, + }, + { + Event: user.HumanPhoneVerificationFailedType, + Reduce: p.reduceHumanPhoneVerificationFailed, + }, + { + Event: user.UserV1PhoneVerificationFailedType, + Reduce: p.reduceHumanPhoneVerificationFailed, + }, + + { + Event: user.HumanAvatarAddedType, + Reduce: p.reduceHumanAvatarAdded, + }, + { + Event: user.HumanAvatarRemovedType, + Reduce: p.reduceHumanAvatarRemoved, + }, + + { + Event: user.HumanPasswordChangedType, + Reduce: p.reduceHumanPasswordChanged, + }, + { + Event: user.UserV1PasswordChangedType, + Reduce: p.reduceHumanPasswordChanged, + }, + { + Event: user.HumanPasswordCodeAddedType, + Reduce: p.reduceHumanPasswordCodeAdded, + }, + { + Event: user.UserV1PasswordCodeAddedType, + Reduce: p.reduceHumanPasswordCodeAdded, + }, + { + Event: user.HumanPasswordCheckSucceededType, + Reduce: p.reduceHumanPasswordCheckSucceeded, + }, + { + Event: user.UserV1PasswordCheckSucceededType, + Reduce: p.reduceHumanPasswordCheckSucceeded, + }, + { + Event: user.HumanPasswordCheckFailedType, + Reduce: p.reduceHumanPasswordCheckFailed, + }, + { + Event: user.UserV1PasswordCheckFailedType, + Reduce: p.reduceHumanPasswordCheckFailed, + }, + { + Event: user.HumanPasswordHashUpdatedType, + Reduce: p.reduceHumanPasswordHashUpdated, + }, + + { + Event: user.MachineAddedEventType, + Reduce: p.reduceMachineAdded, + }, + { + Event: user.MachineChangedEventType, + Reduce: p.reduceMachineChanged, + }, + + { + Event: user.MachineSecretSetType, + Reduce: p.reduceMachineSecretSet, + }, + { + Event: user.MachineSecretHashUpdatedType, + Reduce: p.reduceMachineSecretHashUpdated, + }, + { + Event: user.MachineSecretRemovedType, + Reduce: p.reduceMachineSecretRemoved, + }, + + { + Event: user.MachineKeyAddedEventType, + Reduce: p.reduceMachineKeyAdded, + }, + { + Event: user.MachineKeyRemovedEventType, + Reduce: p.reduceMachineKeyRemoved, + }, + { + Event: user.UserV1MFAInitSkippedType, + Reduce: p.reduceMFAInitSkipped, + }, + { + Event: user.HumanMFAInitSkippedType, + Reduce: p.reduceMFAInitSkipped, + }, + + { + Event: user.PersonalAccessTokenAddedType, + Reduce: p.reducePersonalAccessTokenAdded, + }, + { + Event: user.PersonalAccessTokenRemovedType, + Reduce: p.reducePersonalAccessTokenRemoved, + }, + + { + Event: user.MetadataSetType, + Reduce: p.reduceMetadataSet, + }, + { + Event: user.MetadataRemovedType, + Reduce: p.reduceMetadataRemoved, + }, + { + Event: user.MetadataRemovedAllType, + Reduce: p.reduceMetadataRemovedAll, + }, + { + Event: user.HumanPasswordlessTokenAddedType, + Reduce: p.reducePasskeyAdded, + }, + { + Event: user.HumanPasswordlessTokenVerifiedType, + Reduce: p.reducePasskeyVerified, + }, + { + Event: user.HumanPasswordlessTokenSignCountChangedType, + Reduce: p.reducePasskeySignCountSet, + }, + { + Event: user.HumanPasswordlessTokenRemovedType, + Reduce: p.reducePasskeyRemoved, + }, + + { + Event: user.HumanU2FTokenAddedType, + Reduce: p.reducePasskeyAdded, + }, + { + Event: user.HumanU2FTokenVerifiedType, + Reduce: p.reducePasskeyVerified, + }, + { + Event: user.HumanU2FTokenSignCountChangedType, + Reduce: p.reducePasskeySignCountSet, + }, + { + Event: user.HumanU2FTokenRemovedType, + Reduce: p.reducePasskeyRemoved, + }, + + { + Event: user.HumanPasswordlessInitCodeAddedType, + Reduce: p.reducePasskeyInitCodeAdded, + }, + { + Event: user.HumanPasswordlessInitCodeCheckFailedType, + Reduce: p.reducePasskeyInitCodeCheckFailed, + }, + { + Event: user.HumanPasswordlessInitCodeCheckSucceededType, + Reduce: p.reducePasskeyInitCodeCheckSucceeded, + }, + { + Event: user.HumanPasswordlessInitCodeRequestedType, + Reduce: p.reducePasskeyInitCodeRequested, + }, + { + Event: user.UserIDPLinkAddedType, + Reduce: p.reduceIDPLinkAdded, + }, + { + Event: user.UserIDPLinkCascadeRemovedType, + Reduce: p.reduceIDPLinkCascadeRemoved, + }, + { + Event: user.UserIDPLinkRemovedType, + Reduce: p.reduceIDPLinkRemoved, + }, + { + Event: user.UserIDPExternalIDMigratedType, + Reduce: p.reduceIDPLinkUserIDMigrated, + }, + { + Event: user.UserIDPExternalUsernameChangedType, + Reduce: p.reduceIDPLinkUsernameChanged, + }, + { + Event: user.HumanMFAOTPAddedType, + Reduce: p.reduceTOTPAdded, + }, + { + Event: user.UserV1MFAOTPAddedType, + Reduce: p.reduceTOTPAdded, + }, + { + Event: user.HumanMFAOTPVerifiedType, + Reduce: p.reduceTOTPVerified, + }, + { + Event: user.UserV1MFAOTPVerifiedType, + Reduce: p.reduceTOTPVerified, + }, + { + Event: user.HumanMFAOTPRemovedType, + Reduce: p.reduceTOTPRemoved, + }, + { + Event: user.UserV1MFAOTPRemovedType, + Reduce: p.reduceTOTPRemoved, + }, + { + Event: user.HumanMFAOTPCheckSucceededType, + Reduce: p.reduceTOTPCheckSucceeded, + }, + { + Event: user.UserV1MFAOTPCheckSucceededType, + Reduce: p.reduceTOTPCheckSucceeded, + }, + { + Event: user.HumanMFAOTPCheckFailedType, + Reduce: p.reduceTOTPCheckFailed, + }, + { + Event: user.UserV1MFAOTPCheckFailedType, + Reduce: p.reduceTOTPCheckFailed, + }, + { + Event: user.HumanOTPSMSAddedType, + Reduce: p.reduceOTPSMSEnabled, + }, + { + Event: user.HumanOTPSMSRemovedType, + Reduce: p.reduceOTPSMSDisabled, + }, + { + Event: user.HumanOTPSMSCheckSucceededType, + Reduce: p.reduceOTPSMSCheckSucceeded, + }, + { + Event: user.HumanOTPSMSCheckFailedType, + Reduce: p.reduceOTPSMSCheckFailed, + }, + { + Event: user.HumanOTPEmailAddedType, + Reduce: p.reduceOTPEmailEnabled, + }, + { + Event: user.HumanOTPEmailRemovedType, + Reduce: p.reduceOTPEmailDisabled, + }, + { + Event: user.HumanOTPEmailCheckSucceededType, + Reduce: p.reduceOTPEmailCheckSucceeded, + }, + { + Event: user.HumanOTPEmailCheckFailedType, + Reduce: p.reduceOTPEmailCheckFailed, + }, + { + Event: user.HumanInviteCodeAddedType, + Reduce: p.reduceInviteCodeAdded, + }, + { + Event: user.HumanInviteCheckSucceededType, + Reduce: p.reduceInviteCheckSucceeded, + }, + { + Event: user.HumanInviteCheckFailedType, + Reduce: p.reduceInviteCheckFailed, + }, + }, + }, + } +} + +func (u *userRelationalProjection) reduceHumanAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + v3Tx := v3_sql.SQLTx(tx) + + userRepo := repository.UserRepository() + + var phone *domain.HumanPhone + if e.PhoneNumber != "" { + phone = &domain.HumanPhone{ + UnverifiedNumber: string(e.PhoneNumber), + } + } + + password := domain.HumanPassword{ + IsChangeRequired: e.ChangeRequired, + } + if hash := crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash); hash != "" { + password.Hash = hash + password.ChangedAt = e.CreatedAt() + } + + return userRepo.Create(ctx, v3Tx, &domain.User{ + InstanceID: e.Aggregate().InstanceID, + OrganizationID: e.Aggregate().ResourceOwner, + ID: e.Aggregate().ID, + Username: e.UserName, + State: domain.UserStateActive, + CreatedAt: e.CreatedAt(), + UpdatedAt: e.CreatedAt(), + // TODO check when to set username unique + // IsUsernameOrgUnique: , + Human: &domain.HumanUser{ + FirstName: e.FirstName, + LastName: e.LastName, + Nickname: e.NickName, + DisplayName: e.DisplayName, + PreferredLanguage: e.PreferredLanguage, + Gender: mapHumanGender(e.Gender), + Email: domain.HumanEmail{ + UnverifiedAddress: string(e.EmailAddress), + }, + Phone: phone, + Password: password, + }, + }) + }), nil +} + +func (p *userRelationalProjection) reduceHumanRegistered(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanRegisteredEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + v3Tx := v3_sql.SQLTx(tx) + + userRepo := repository.UserRepository() + + var phone *domain.HumanPhone + if e.PhoneNumber != "" { + phone = &domain.HumanPhone{ + UnverifiedNumber: string(e.PhoneNumber), + } + } + + password := domain.HumanPassword{ + IsChangeRequired: e.ChangeRequired, + } + if hash := crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash); hash != "" { + password.Hash = hash + password.ChangedAt = e.CreatedAt() + } + + return userRepo.Create(ctx, v3Tx, &domain.User{ + InstanceID: e.Aggregate().InstanceID, + OrganizationID: e.Aggregate().ResourceOwner, + ID: e.Aggregate().ID, + Username: e.UserName, + State: domain.UserStateActive, + CreatedAt: e.CreatedAt(), + UpdatedAt: e.CreatedAt(), + // TODO check when to set username unique + // IsUsernameOrgUnique: , + Human: &domain.HumanUser{ + FirstName: e.FirstName, + LastName: e.LastName, + Nickname: e.NickName, + DisplayName: e.DisplayName, + PreferredLanguage: e.PreferredLanguage, + Gender: mapHumanGender(e.Gender), + Email: domain.HumanEmail{ + UnverifiedAddress: string(e.EmailAddress), + }, + Phone: phone, + Password: password, + }, + }) + }), nil +} + +func (p *userRelationalProjection) reduceUserLocked(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserLockedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + repo.SetState(domain.UserStateLocked), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceUserUnlocked(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserUnlockedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + repo.SetState(domain.UserStateActive), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceUserDeactivated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserDeactivatedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + repo.SetState(domain.UserStateInactive), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceUserReactivated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserReactivatedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + repo.SetState(domain.UserStateActive), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceUserRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserRemovedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Delete( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceUsernameChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UsernameChangedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update( + ctx, + v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + repo.SetUsername(e.UserName), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceDomainClaimed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.DomainClaimedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + userRepo := repository.UserRepository() + + _, err := userRepo.Update(ctx, v3_sql.SQLTx(tx), + userRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + userRepo.SetUsername(e.UserName), + userRepo.SetUpdatedAt(e.CreatedAt()), + ) + + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanProfileChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanProfileChangedEvent](event) + if err != nil { + return nil, err + } + + repo := repository.UserRepository().Human() + changes := make([]database.Change, 0, 7) + + if e.FirstName != "" { + changes = append(changes, repo.SetFirstName(e.FirstName)) + } + + if e.LastName != "" { + changes = append(changes, repo.SetLastName(e.LastName)) + } + + if e.NickName != nil { + changes = append(changes, repo.SetNickname(*e.NickName)) + } + + if e.DisplayName != nil { + changes = append(changes, repo.SetDisplayName(*e.DisplayName)) + } + + if e.PreferredLanguage != nil { + changes = append(changes, repo.SetPreferredLanguage(*e.PreferredLanguage)) + } + + if e.Gender != nil { + changes = append(changes, repo.SetGender(mapHumanGender(*e.Gender))) + } + changes = append(changes, repo.SetUpdatedAt(e.CreatedAt())) + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Agg.InstanceID, e.Aggregate().ID), + changes..., + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPhoneChangedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetUnverifiedPhone(string(e.PhoneNumber)), + repo.SetPhoneVerification(&domain.VerificationTypeInit{ + CreatedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPhoneRemovedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemovePhone(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPhoneVerified(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPhoneVerifiedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetPhoneVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPhoneCodeAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPhoneCodeAddedEvent](event) + if err != nil { + return nil, err + } + + var expiry *time.Duration + if e.Expiry > 0 { + expiry = &e.Expiry + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetPhoneVerification(&domain.VerificationTypeUpdate{ + Code: e.Code, + Expiry: expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPhoneVerificationFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPhoneVerificationFailedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetPhoneVerification(&domain.VerificationTypeFailed{ + FailedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanEmailChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanEmailChangedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetUnverifiedEmail(string(e.EmailAddress)), + repo.SetEmailVerification(&domain.VerificationTypeInit{ + CreatedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanEmailVerified(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanEmailVerifiedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetEmailVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanEmailCodeAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanEmailCodeAddedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + + var expiry *time.Duration + if e.Expiry > 0 { + expiry = &e.Expiry + } + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetEmailVerification(&domain.VerificationTypeUpdate{ + Code: e.Code, + Expiry: expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanEmailVerificationFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanEmailVerificationFailedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetEmailVerification(&domain.VerificationTypeFailed{ + FailedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanAvatarAddedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetAvatarKey(&e.StoreKey), + repo.SetUpdatedAt(e.CreationDate()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanAvatarRemovedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Human() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetAvatarKey(nil), + repo.SetUpdatedAt(e.CreationDate()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPasswordChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordChangedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetPasswordChangeRequired(e.ChangeRequired), + repo.SetPassword(crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)), + repo.SetResetPasswordVerification(&domain.VerificationTypeSucceeded{VerifiedAt: e.CreatedAt()}), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPasswordCodeAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordCodeAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + var expiry *time.Duration + if e.Expiry > 0 { + expiry = &e.Expiry + } + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetResetPasswordVerification(&domain.VerificationTypeUpdate{ + Code: e.Code, + Expiry: expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPasswordCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetLastSuccessfulPasswordCheck(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPasswordCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.IncrementPasswordFailedAttempts(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceHumanPasswordHashUpdated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordHashUpdatedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetPassword(e.EncodedHash), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineSecretSet(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineSecretSetEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.MachineUserRepository() + + secret := crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret) + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetSecret(&secret), + repo.SetUpdatedAt(e.CreationDate()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineSecretHashUpdated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineSecretHashUpdatedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Machine() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetSecret(&e.HashedSecret), + repo.SetUpdatedAt(e.CreationDate()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineSecretRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineSecretRemovedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository().Machine() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetSecret(nil), + repo.SetUpdatedAt(e.CreationDate()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineKeyAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineKeyAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.MachineUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.AddKey(&domain.MachineKey{ + ID: e.KeyID, + Type: mapMachineKeyType(e.KeyType), + PublicKey: e.PublicKey, + CreatedAt: e.CreatedAt(), + ExpiresAt: e.ExpirationDate, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineKeyRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineKeyRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.MachineUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveKey(e.KeyID), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMachineAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineAddedEvent](event) + if err != nil { + return nil, err + } + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + return repo.Create(ctx, v3_sql.SQLTx(tx), + &domain.User{ + ID: e.Aggregate().ID, + InstanceID: e.Aggregate().InstanceID, + OrganizationID: e.Aggregate().ResourceOwner, + Username: e.UserName, + // TODO check when to set username unique + // IsUsernameOrgUnique: , + State: domain.UserStateActive, + CreatedAt: e.CreatedAt(), + UpdatedAt: e.CreatedAt(), + Machine: &domain.MachineUser{ + Name: e.Name, + Description: e.Description, + AccessTokenType: mapMachineAccessTokenType(e.AccessTokenType), + }, + }, + ) + }), nil +} + +func (p *userRelationalProjection) reduceMachineChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MachineChangedEvent](event) + if err != nil { + return nil, err + } + repo := repository.MachineUserRepository() + + changes := make([]database.Change, 0, 4) + if e.Name != nil { + changes = append(changes, repo.SetName(*e.Name)) + } + if e.Description != nil { + changes = append(changes, repo.SetDescription(*e.Description)) + } + if e.AccessTokenType != nil { + changes = append(changes, repo.SetAccessTokenType(mapMachineAccessTokenType(*e.AccessTokenType))) + } + changes = append(changes, repo.SetUpdatedAt(e.CreatedAt())) + + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + database.And( + repo.InstanceIDCondition(e.Aggregate().InstanceID), + repo.IDCondition(e.Aggregate().ID), + ), + changes..., + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePersonalAccessTokenAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.PersonalAccessTokenAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.MachineUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.AddPersonalAccessToken(&domain.PersonalAccessToken{ + ID: e.TokenID, + Scopes: e.Scopes, + ExpiresAt: e.Expiration, + CreatedAt: e.CreatedAt(), + }), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePersonalAccessTokenRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.PersonalAccessTokenRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.MachineUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemovePersonalAccessToken(e.TokenID), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMetadataSet(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MetadataSetEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-xg4IJ", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetMetadata(&domain.Metadata{ + Key: e.Key, + Value: e.Value, + CreatedAt: e.CreationDate(), + UpdatedAt: e.CreationDate(), + }), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMetadataRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MetadataRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-xg4IJ", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveMetadata(repo.MetadataConditions().MetadataKeyCondition(database.TextOperationEqual, e.Key)), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMetadataRemovedAll(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.MetadataRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-xg4IJ", "reduce.wrong.db.pool %T", ex) + } + repo := repository.UserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveMetadata(nil), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyAdded(event eventstore.Event) (*handler.Statement, error) { + var ( + e user.HumanWebAuthNAddedEvent + typ domain.PasskeyType + ) + switch typed := event.(type) { + case *user.HumanPasswordlessAddedEvent: + e = typed.HumanWebAuthNAddedEvent + typ = domain.PasskeyTypePasswordless + case *user.HumanU2FAddedEvent: + e = typed.HumanWebAuthNAddedEvent + typ = domain.PasskeyTypeU2F + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-nd7f3", "reduce.wrong.event.type for passkey %s", event.Type()) + } + return handler.NewStatement(&e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + database.And( + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + ), + repo.AddPasskey(&domain.Passkey{ + ID: e.WebAuthNTokenID, + Challenge: []byte(e.Challenge), + RelyingPartyID: e.RPID, + CreatedAt: e.CreatedAt(), + UpdatedAt: e.CreatedAt(), + Type: typ, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyVerified(event eventstore.Event) (*handler.Statement, error) { + var ( + e user.HumanWebAuthNVerifiedEvent + ) + switch typed := event.(type) { + case *user.HumanPasswordlessVerifiedEvent: + e = typed.HumanWebAuthNVerifiedEvent + case *user.HumanU2FVerifiedEvent: + e = typed.HumanWebAuthNVerifiedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-nd7f3", "reduce.wrong.event.type for passkey %s", event.Type()) + } + return handler.NewStatement(&e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.UpdatePasskey( + repo.PasskeyConditions().IDCondition(e.WebAuthNTokenID), + repo.SetPasskeyKeyID(e.KeyID), + repo.SetPasskeyPublicKey(e.PublicKey), + repo.SetPasskeyAttestationType(e.AttestationType), + repo.SetPasskeyAuthenticatorAttestationGUID(e.AAGUID), + repo.SetPasskeySignCount(e.SignCount), + repo.SetPasskeyName(e.WebAuthNTokenName), + repo.SetPasskeyUpdatedAt(e.CreatedAt()), + ), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeySignCountSet(event eventstore.Event) (*handler.Statement, error) { + var ( + e user.HumanWebAuthNSignCountChangedEvent + ) + switch typed := event.(type) { + case *user.HumanPasswordlessSignCountChangedEvent: + e = typed.HumanWebAuthNSignCountChangedEvent + case *user.HumanU2FSignCountChangedEvent: + e = typed.HumanWebAuthNSignCountChangedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-nd7f3", "reduce.wrong.event.type for passkey %s", event.Type()) + } + return handler.NewStatement(&e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.UpdatePasskey( + repo.PasskeyConditions().IDCondition(e.WebAuthNTokenID), + repo.SetPasskeySignCount(e.SignCount), + ), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyRemoved(event eventstore.Event) (*handler.Statement, error) { + var ( + e user.HumanWebAuthNRemovedEvent + ) + switch typed := event.(type) { + case *user.HumanPasswordlessRemovedEvent: + e = typed.HumanWebAuthNRemovedEvent + case *user.HumanU2FRemovedEvent: + e = typed.HumanWebAuthNRemovedEvent + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-nd7f3", "reduce.wrong.event.type for passkey %s", event.Type()) + } + return handler.NewStatement(&e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + userRepo := repository.UserRepository() + humanRepo := userRepo.Human() + _, err := userRepo.Update(ctx, v3_sql.SQLTx(tx), + userRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + humanRepo.RemovePasskey(humanRepo.PasskeyConditions().IDCondition(e.WebAuthNTokenID)), + humanRepo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyInitCodeAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordlessInitCodeAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetVerification(&domain.VerificationTypeInit{ + ID: &e.ID, + Code: e.Code, + CreatedAt: e.CreatedAt(), + Expiry: &e.Expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyInitCodeCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordlessInitCodeCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetVerification(&domain.VerificationTypeFailed{ + ID: &e.ID, + FailedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyInitCodeCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordlessInitCodeCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetVerification(&domain.VerificationTypeSucceeded{ + ID: &e.ID, + VerifiedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reducePasskeyInitCodeRequested(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanPasswordlessInitCodeRequestedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetVerification(&domain.VerificationTypeInit{ + ID: &e.ID, + Code: e.Code, + CreatedAt: e.CreatedAt(), + Expiry: &e.Expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceMFAInitSkipped(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanMFAInitSkippedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SkipMultifactorInitializationAt(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceIDPLinkAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPLinkAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.AddIdentityProviderLink(&domain.IdentityProviderLink{ + ProviderID: e.IDPConfigID, + ProvidedUserID: e.ExternalUserID, + ProvidedUsername: e.DisplayName, + CreatedAt: e.CreatedAt(), + UpdatedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceIDPLinkCascadeRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPLinkCascadeRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveIdentityProviderLink(e.IDPConfigID, e.ExternalUserID), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceIDPLinkRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPLinkRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveIdentityProviderLink(e.IDPConfigID, e.ExternalUserID), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceIDPLinkUserIDMigrated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPExternalIDMigratedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.UpdateIdentityProviderLink( + database.And(repo.IdentityProviderLinkConditions().ProviderIDCondition(e.IDPConfigID), repo.IdentityProviderLinkConditions().ProvidedUserIDCondition(e.PreviousID)), + repo.SetIdentityProviderLinkProvidedID(e.NewID), + ), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceIDPLinkUsernameChanged(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPExternalUsernameEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.UpdateIdentityProviderLink( + database.And(repo.IdentityProviderLinkConditions().ProviderIDCondition(e.IDPConfigID), repo.IdentityProviderLinkConditions().ProvidedUserIDCondition(e.ExternalUserID)), + repo.SetIdentityProviderLinkUsername(e.ExternalUsername), + ), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceTOTPAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetTOTPSecret(e.Secret), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceTOTPVerified(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPVerifiedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetTOTPVerifiedAt(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceTOTPRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.RemoveTOTP(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceTOTPCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetLastSuccessfulTOTPCheck(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceTOTPCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.IncrementTOTPFailedAttempts(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPSMSEnabled(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPSMSAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.EnableSMSOTPAt(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPSMSDisabled(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPSMSRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.DisableSMSOTP(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPSMSCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPSMSCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetLastSuccessfulSMSOTPCheck(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPSMSCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPSMSCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.IncrementSMSOTPFailedAttempts(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPEmailEnabled(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPEmailAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.EnableEmailOTPAt(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPEmailDisabled(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPEmailRemovedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.DisableEmailOTP(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPEmailCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPEmailCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetLastSuccessfulEmailOTPCheck(e.CreatedAt()), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceOTPEmailCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanOTPEmailCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.IncrementEmailOTPFailedAttempts(), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceInviteCodeAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanInviteCodeAddedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + var expiry *time.Duration + if e.Expiry > 0 { + expiry = &e.Expiry + } + + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetInviteVerification(&domain.VerificationTypeInit{ + CreatedAt: e.CreatedAt(), + Code: e.Code, + Expiry: expiry, + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceInviteCheckSucceeded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanInviteCheckSucceededEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetInviteVerification(&domain.VerificationTypeSucceeded{ + VerifiedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func (p *userRelationalProjection) reduceInviteCheckFailed(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.HumanInviteCheckFailedEvent](event) + if err != nil { + return nil, err + } + return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error { + tx, ok := ex.(*sql.Tx) + if !ok { + return zerrors.ThrowInvalidArgumentf(nil, "HANDL-iZGH3", "reduce.wrong.db.pool %T", ex) + } + + repo := repository.HumanUserRepository() + _, err := repo.Update(ctx, v3_sql.SQLTx(tx), + repo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ID), + repo.SetInviteVerification(&domain.VerificationTypeFailed{ + FailedAt: e.CreatedAt(), + }), + repo.SetUpdatedAt(e.CreatedAt()), + ) + return err + }), nil +} + +func mapHumanGender(gender old_domain.Gender) domain.HumanGender { + switch gender { + case old_domain.GenderFemale: + return domain.HumanGenderFemale + case old_domain.GenderMale: + return domain.HumanGenderMale + case old_domain.GenderDiverse: + return domain.HumanGenderDiverse + case old_domain.GenderUnspecified: + fallthrough + default: + return domain.HumanGenderUnspecified + } +} + +func mapMachineAccessTokenType(tokenType old_domain.OIDCTokenType) domain.AccessTokenType { + switch tokenType { + case old_domain.OIDCTokenTypeBearer: + return domain.AccessTokenTypeBearer + case old_domain.OIDCTokenTypeJWT: + return domain.AccessTokenTypeJWT + default: + return domain.AccessTokenTypeUnspecified + } +} + +func mapMachineKeyType(keyType old_domain.AuthNKeyType) domain.MachineKeyType { + switch keyType { + case old_domain.AuthNKeyTypeJSON: + return domain.MachineKeyTypeJSON + case old_domain.AuthNKeyTypeNONE: + fallthrough + default: + return domain.MachineKeyTypeNone + } +} diff --git a/internal/repository/user/human_email.go b/internal/repository/user/human_email.go index 4caf56453..3ff469249 100644 --- a/internal/repository/user/human_email.go +++ b/internal/repository/user/human_email.go @@ -18,7 +18,6 @@ const ( HumanEmailVerificationFailedType = emailEventPrefix + "verification.failed" HumanEmailCodeAddedType = emailEventPrefix + "code.added" HumanEmailCodeSentType = emailEventPrefix + "code.sent" - HumanEmailConfirmURLAddedType = emailEventPrefix + "confirm_url.added" ) type HumanEmailChangedEvent struct {