From d25454b84b4d1cde24dbd097ad73636fd8a74451 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 11 Apr 2023 17:07:32 +0200 Subject: [PATCH 01/76] feat: allow skip of success page for native apps (#5627) add possibility to return to callback directly after login without rendering the successful login page --- .../apps/app-create/app-create.component.html | 2 +- .../apps/app-detail/app-detail.component.html | 17 +- .../apps/app-detail/app-detail.component.ts | 19 +- console/src/assets/i18n/de.json | 2 + console/src/assets/i18n/en.json | 2 + console/src/assets/i18n/fr.json | 2 + console/src/assets/i18n/it.json | 2 + console/src/assets/i18n/ja.json | 2 + console/src/assets/i18n/pl.json | 2 + console/src/assets/i18n/zh.json | 2 + .../guides/manage/console/applications.mdx | 1 + internal/api/grpc/admin/export.go | 1 + .../project_application_converter.go | 2 + internal/api/grpc/project/application.go | 1 + .../api/ui/login/login_success_handler.go | 32 +- .../static/resources/scripts/login_success.js | 7 - .../login/static/templates/login_success.html | 2 +- .../eventsourcing/eventstore/auth_request.go | 2 +- internal/command/instance_domain_test.go | 4 +- internal/command/project_application_oidc.go | 38 +- .../command/project_application_oidc_model.go | 10 + .../command/project_application_oidc_test.go | 23 +- internal/command/project_converter.go | 9 +- internal/command/user_human_otp.go | 4 +- internal/domain/application_oidc.go | 1 + internal/query/app.go | 75 +- internal/query/app_test.go | 661 +++++++++++------- internal/query/projection/app.go | 8 +- internal/query/projection/app_test.go | 70 +- internal/repository/project/oidc_config.go | 13 +- proto/zitadel/app.proto | 5 + proto/zitadel/management.proto | 10 + 32 files changed, 641 insertions(+), 390 deletions(-) diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index e078c5a67..02e47ff6a 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -305,7 +305,7 @@ - {{ 'APP.API.AUTHMETHOD.' + authMethodType?.value | translate }} + {{ 'APP.API.AUTHMETHOD.' + apiAppRequest.toObject().authMethodType | translate }} diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index bc18c9bcf..f910e129d 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -331,11 +331,24 @@ + + {{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE' | translate }} + + {{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION' | translate }} + + | null { + return this.oidcForm.get('devMode') as FormControl; + } + + public get skipNativeAppSuccessPage(): FormControl | null { + return this.oidcForm.get('skipNativeAppSuccessPage') as FormControl; } public get accessTokenType(): AbstractControl | null { diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index c4277b036..7a43894f3 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1930,6 +1930,8 @@ "REGENERATESECRET": "Client Secret neu generieren", "DEVMODE": "Entwicklermodus", "DEVMODEDESC": "Bei eingeschaltetem Entwicklermodus werden die Weiterleitungs-URIs im OIDC-Flow nicht validiert.", + "SKIPNATIVEAPPSUCCESSPAGE": "Login Erfolgseite überspringen", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Erfolgseite nach dem Login für diese Native Applikation überspringen.", "REDIRECT": "Weiterleitungs-URIs", "REDIRECTSECTION": "Weiterleitungs-URIs", "POSTLOGOUTREDIRECT": "URIs für Post-Log-out", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 41039461f..23b0233b3 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1923,6 +1923,8 @@ "REGENERATESECRET": "Regenerate Client Secret", "DEVMODE": "Development Mode", "DEVMODEDESC": "Beware: With development mode enabled redirect URIs will not be validated.", + "SKIPNATIVEAPPSUCCESSPAGE": "Skip Login Success Page", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Skip the success page after a login for this native app.", "REDIRECT": "Redirect URIs", "REDIRECTSECTION": "Redirect URIs", "POSTLOGOUTREDIRECT": "Post Logout URIs", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 5e516d343..1adc3b2cb 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1931,6 +1931,8 @@ "REGENERATESECRET": "Régénérer le secret du client", "DEVMODE": "Mode développement", "DEVMODEDESC": "Attention", + "SKIPNATIVEAPPSUCCESSPAGE": "Sauter la page de succès de connexion", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Sauter la page de succès après la connexion pour cette application native", "REDIRECT": "Rediriger les URI", "REDIRECTSECTION": "URI de redirection", "POSTLOGOUTREDIRECT": "URIs de post-déconnexion", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 12ad95538..31f1b545e 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1932,6 +1932,8 @@ "REGENERATESECRET": "Rigenera il Client Secret", "DEVMODE": "Modalit\u00e0 di sviluppo (DEV Mode)", "DEVMODEDESC": "Attenzione: Con la modalit\u00e0 di sviluppo abilitata, gli URI di reindirizzamento non saranno convalidati.", + "SKIPNATIVEAPPSUCCESSPAGE": "Salta la pagina di successo dopo il login", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Salta la pagina di successo dopo il login per questa applicazione nativa", "REDIRECT": "URI per il reindrizzamento", "REDIRECTSECTION": "Reindirizzamento", "POSTLOGOUTREDIRECT": "URI post logout", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index bab05f88a..5c3a235c0 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1922,6 +1922,8 @@ "REGENERATESECRET": "クライアントシークレットを再生成する", "DEVMODE": "開発モード", "DEVMODEDESC": "注意:開発モードを有効にすると、URIが認証されません。", + "SKIPNATIVEAPPSUCCESSPAGE": "ログイン後に成功ページをスキップする", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "このネイティブアプリのログイン後に成功ページをスキップする", "REDIRECT": "リダイレクトURI", "REDIRECTSECTION": "リダイレクトURI", "POSTLOGOUTREDIRECT": "ログアウトURI", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 25bcb24da..34e649b70 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1931,6 +1931,8 @@ "REGENERATESECRET": "Odtwórz sekret klienta", "DEVMODE": "Tryb rozwoju", "DEVMODEDESC": "Uwaga: przy włączonym trybie rozwoju adresy URI przekierowania nie będą sprawdzane.", + "SKIPNATIVEAPPSUCCESSPAGE": "Pomiń stronę sukcesu po zalogowaniu", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Pomiń stronę sukcesu po zalogowaniu dla tej Natywny aplikację", "REDIRECT": "Adresy URI przekierowania", "REDIRECTSECTION": "Adresy URI przekierowania", "POSTLOGOUTREDIRECT": "Adresy URI po wylogowaniu", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 226064f36..fa6d50690 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1930,6 +1930,8 @@ "REGENERATESECRET": "重新生成客户端密钥", "DEVMODE": "开发模式", "DEVMODEDESC": "注意:启用开发模式的重定向 URI 将不会被验证。", + "SKIPNATIVEAPPSUCCESSPAGE": "登录后跳过成功页面", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "登录后跳过本机应用的成功页面", "REDIRECT": "重定向 URLs", "REDIRECTSECTION": "重定向 URLs", "POSTLOGOUTREDIRECT": "退出登录重定向 URLs", diff --git a/docs/docs/guides/manage/console/applications.mdx b/docs/docs/guides/manage/console/applications.mdx index 0266c5ae4..8cad1c9eb 100644 --- a/docs/docs/guides/manage/console/applications.mdx +++ b/docs/docs/guides/manage/console/applications.mdx @@ -142,6 +142,7 @@ On the bottom you can optionally set a **ClockSkew** time which is added to the Like on creation, you can modify you redirect settings here. Note that for local development you most likely have to enable development mode, as redirects to http:// are otherwise blocked. +On Native Apps you can also skip the Login Success Page. Redirect URIs {{end}} -{{template "main-bottom" .}} \ No newline at end of file +{{template "main-bottom" .}} diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 4b1ca8891..69d825fb8 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -1224,7 +1224,7 @@ func (repo *AuthRequestRepo) hasSucceededPage(ctx context.Context, request *doma if err != nil { return false, err } - return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative, nil + return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative && !app.OIDCConfig.SkipNativeAppSuccessPage, nil } func (repo *AuthRequestRepo) getDomainPolicy(ctx context.Context, orgID string) (*query.DomainPolicy, error) { diff --git a/internal/command/instance_domain_test.go b/internal/command/instance_domain_test.go index 6cd03a28e..90a692519 100644 --- a/internal/command/instance_domain_test.go +++ b/internal/command/instance_domain_test.go @@ -161,7 +161,9 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + false, + ), ), ), expectPush( diff --git a/internal/command/project_application_oidc.go b/internal/command/project_application_oidc.go index 99eec03d4..44efd279a 100644 --- a/internal/command/project_application_oidc.go +++ b/internal/command/project_application_oidc.go @@ -19,20 +19,21 @@ import ( type addOIDCApp struct { AddApp - Version domain.OIDCVersion - RedirectUris []string - ResponseTypes []domain.OIDCResponseType - GrantTypes []domain.OIDCGrantType - ApplicationType domain.OIDCApplicationType - AuthMethodType domain.OIDCAuthMethodType - PostLogoutRedirectUris []string - DevMode bool - AccessTokenType domain.OIDCTokenType - AccessTokenRoleAssertion bool - IDTokenRoleAssertion bool - IDTokenUserinfoAssertion bool - ClockSkew time.Duration - AdditionalOrigins []string + Version domain.OIDCVersion + RedirectUris []string + ResponseTypes []domain.OIDCResponseType + GrantTypes []domain.OIDCGrantType + ApplicationType domain.OIDCApplicationType + AuthMethodType domain.OIDCAuthMethodType + PostLogoutRedirectUris []string + DevMode bool + AccessTokenType domain.OIDCTokenType + AccessTokenRoleAssertion bool + IDTokenRoleAssertion bool + IDTokenUserinfoAssertion bool + ClockSkew time.Duration + AdditionalOrigins []string + SkipSuccessPageForNativeApp bool ClientID string ClientSecret *crypto.CryptoValue @@ -109,6 +110,7 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has app.IDTokenUserinfoAssertion, app.ClockSkew, app.AdditionalOrigins, + app.SkipSuccessPageForNativeApp, ), }, nil }, nil @@ -191,7 +193,9 @@ func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain oidcApp.IDTokenRoleAssertion, oidcApp.IDTokenUserinfoAssertion, oidcApp.ClockSkew, - oidcApp.AdditionalOrigins)) + oidcApp.AdditionalOrigins, + oidcApp.SkipNativeAppSuccessPage, + )) addedApplication.AppID = oidcApp.AppID pushedEvents, err := c.eventstore.Push(ctx, events...) @@ -241,7 +245,9 @@ func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCA oidc.IDTokenRoleAssertion, oidc.IDTokenUserinfoAssertion, oidc.ClockSkew, - oidc.AdditionalOrigins) + oidc.AdditionalOrigins, + oidc.SkipNativeAppSuccessPage, + ) if err != nil { return nil, err } diff --git a/internal/command/project_application_oidc_model.go b/internal/command/project_application_oidc_model.go index d88c79a4b..7b70d8955 100644 --- a/internal/command/project_application_oidc_model.go +++ b/internal/command/project_application_oidc_model.go @@ -35,6 +35,7 @@ type OIDCApplicationWriteModel struct { ClockSkew time.Duration State domain.AppState AdditionalOrigins []string + SkipNativeAppSuccessPage bool oidc bool } @@ -156,6 +157,7 @@ func (wm *OIDCApplicationWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAdd wm.IDTokenUserinfoAssertion = e.IDTokenUserinfoAssertion wm.ClockSkew = e.ClockSkew wm.AdditionalOrigins = e.AdditionalOrigins + wm.SkipNativeAppSuccessPage = e.SkipNativeAppSuccessPage } func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfigChangedEvent) { @@ -201,6 +203,9 @@ func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfig if e.AdditionalOrigins != nil { wm.AdditionalOrigins = *e.AdditionalOrigins } + if e.SkipNativeAppSuccessPage != nil { + wm.SkipNativeAppSuccessPage = *e.SkipNativeAppSuccessPage + } } func (wm *OIDCApplicationWriteModel) Query() *eventstore.SearchQueryBuilder { @@ -240,6 +245,7 @@ func (wm *OIDCApplicationWriteModel) NewChangedEvent( idTokenUserinfoAssertion bool, clockSkew time.Duration, additionalOrigins []string, + skipNativeAppSuccessPage bool, ) (*project.OIDCConfigChangedEvent, bool, error) { changes := make([]project.OIDCConfigChanges, 0) var err error @@ -286,6 +292,10 @@ func (wm *OIDCApplicationWriteModel) NewChangedEvent( if !reflect.DeepEqual(wm.AdditionalOrigins, additionalOrigins) { changes = append(changes, project.ChangeAdditionalOrigins(additionalOrigins)) } + if wm.SkipNativeAppSuccessPage != skipNativeAppSuccessPage { + changes = append(changes, project.ChangeSkipNativeAppSuccessPage(skipNativeAppSuccessPage)) + } + if len(changes) == 0 { return nil, false, nil } diff --git a/internal/command/project_application_oidc_test.go b/internal/command/project_application_oidc_test.go index d8ed4bb80..86b3c6e04 100644 --- a/internal/command/project_application_oidc_test.go +++ b/internal/command/project_application_oidc_test.go @@ -169,6 +169,7 @@ func TestAddOIDCApp(t *testing.T) { false, 0, nil, + false, ), }, }, @@ -325,7 +326,9 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), }, uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), @@ -354,6 +357,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", secretGenerator: GetMockSecretGenerator(t), @@ -382,6 +386,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, State: domain.AppStateActive, Compliance: &domain.Compliance{}, }, @@ -558,7 +563,9 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), ), ), @@ -585,6 +592,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", }, @@ -629,7 +637,9 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), ), expectPush( @@ -666,6 +676,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: false, ClockSkew: time.Second * 2, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", }, @@ -692,6 +703,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: false, ClockSkew: time.Second * 2, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, Compliance: &domain.Compliance{}, State: domain.AppStateActive, }, @@ -826,7 +838,9 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + false, + ), ), ), expectPush( @@ -877,6 +891,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: false, State: domain.AppStateActive, }, }, diff --git a/internal/command/project_converter.go b/internal/command/project_converter.go index 02a1b9eac..35679d8a1 100644 --- a/internal/command/project_converter.go +++ b/internal/command/project_converter.go @@ -25,14 +25,6 @@ func projectGrantWriteModelToProjectGrant(writeModel *ProjectGrantWriteModel) *d } } -func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) domain.Application { - return &domain.ChangeApp{ - AppID: writeModel.AppID, - AppName: writeModel.Name, - State: writeModel.State, - } -} - func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.OIDCApp { return &domain.OIDCApp{ ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), @@ -54,6 +46,7 @@ func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.O IDTokenUserinfoAssertion: writeModel.IDTokenUserinfoAssertion, ClockSkew: writeModel.ClockSkew, AdditionalOrigins: writeModel.AdditionalOrigins, + SkipNativeAppSuccessPage: writeModel.SkipNativeAppSuccessPage, } } diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index 3ee507f73..02dbdae55 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -3,9 +3,9 @@ package command import ( "context" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/v1/models" diff --git a/internal/domain/application_oidc.go b/internal/domain/application_oidc.go index 808052c05..ad08b18d7 100644 --- a/internal/domain/application_oidc.go +++ b/internal/domain/application_oidc.go @@ -45,6 +45,7 @@ type OIDCApp struct { IDTokenUserinfoAssertion bool ClockSkew time.Duration AdditionalOrigins []string + SkipNativeAppSuccessPage bool State AppState } diff --git a/internal/query/app.go b/internal/query/app.go index e08d25e09..6b40f712e 100644 --- a/internal/query/app.go +++ b/internal/query/app.go @@ -40,23 +40,24 @@ type App struct { } type OIDCApp struct { - RedirectURIs database.StringArray - ResponseTypes database.EnumArray[domain.OIDCResponseType] - GrantTypes database.EnumArray[domain.OIDCGrantType] - AppType domain.OIDCApplicationType - ClientID string - AuthMethodType domain.OIDCAuthMethodType - PostLogoutRedirectURIs database.StringArray - Version domain.OIDCVersion - ComplianceProblems database.StringArray - IsDevMode bool - AccessTokenType domain.OIDCTokenType - AssertAccessTokenRole bool - AssertIDTokenRole bool - AssertIDTokenUserinfo bool - ClockSkew time.Duration - AdditionalOrigins database.StringArray - AllowedOrigins database.StringArray + RedirectURIs database.StringArray + ResponseTypes database.EnumArray[domain.OIDCResponseType] + GrantTypes database.EnumArray[domain.OIDCGrantType] + AppType domain.OIDCApplicationType + ClientID string + AuthMethodType domain.OIDCAuthMethodType + PostLogoutRedirectURIs database.StringArray + Version domain.OIDCVersion + ComplianceProblems database.StringArray + IsDevMode bool + AccessTokenType domain.OIDCTokenType + AssertAccessTokenRole bool + AssertIDTokenRole bool + AssertIDTokenUserinfo bool + ClockSkew time.Duration + AdditionalOrigins database.StringArray + AllowedOrigins database.StringArray + SkipNativeAppSuccessPage bool } type SAMLApp struct { @@ -241,6 +242,10 @@ var ( name: projection.AppOIDCConfigColumnAdditionalOrigins, table: appOIDCConfigsTable, } + AppOIDCConfigColumnSkipNativeAppSuccessPage = Column{ + name: projection.AppOIDCConfigColumnSkipNativeAppSuccessPage, + table: appOIDCConfigsTable, + } ) func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string, withOwnerRemoved bool) (_ *App, err error) { @@ -535,6 +540,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(), AppOIDCConfigColumnClockSkew.identifier(), AppOIDCConfigColumnAdditionalOrigins.identifier(), + AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(), AppSAMLConfigColumnAppID.identifier(), AppSAMLConfigColumnEntityID.identifier(), @@ -583,6 +589,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, &oidcConfig.iDTokenUserinfoAssertion, &oidcConfig.clockSkew, &oidcConfig.additionalOrigins, + &oidcConfig.skipNativeAppSuccessPage, &samlConfig.appID, &samlConfig.entityID, @@ -703,6 +710,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(), AppOIDCConfigColumnClockSkew.identifier(), AppOIDCConfigColumnAdditionalOrigins.identifier(), + AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(), AppSAMLConfigColumnAppID.identifier(), AppSAMLConfigColumnEntityID.identifier(), @@ -754,6 +762,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder &oidcConfig.iDTokenUserinfoAssertion, &oidcConfig.clockSkew, &oidcConfig.additionalOrigins, + &oidcConfig.skipNativeAppSuccessPage, &samlConfig.appID, &samlConfig.entityID, @@ -825,6 +834,7 @@ type sqlOIDCConfig struct { additionalOrigins database.StringArray responseTypes database.EnumArray[domain.OIDCResponseType] grantTypes database.EnumArray[domain.OIDCGrantType] + skipNativeAppSuccessPage sql.NullBool } func (c sqlOIDCConfig) set(app *App) { @@ -832,21 +842,22 @@ func (c sqlOIDCConfig) set(app *App) { return } app.OIDCConfig = &OIDCApp{ - Version: domain.OIDCVersion(c.version.Int32), - ClientID: c.clientID.String, - RedirectURIs: c.redirectUris, - AppType: domain.OIDCApplicationType(c.applicationType.Int16), - AuthMethodType: domain.OIDCAuthMethodType(c.authMethodType.Int16), - PostLogoutRedirectURIs: c.postLogoutRedirectUris, - IsDevMode: c.devMode.Bool, - AccessTokenType: domain.OIDCTokenType(c.accessTokenType.Int16), - AssertAccessTokenRole: c.accessTokenRoleAssertion.Bool, - AssertIDTokenRole: c.iDTokenRoleAssertion.Bool, - AssertIDTokenUserinfo: c.iDTokenUserinfoAssertion.Bool, - ClockSkew: time.Duration(c.clockSkew.Int64), - AdditionalOrigins: c.additionalOrigins, - ResponseTypes: c.responseTypes, - GrantTypes: c.grantTypes, + Version: domain.OIDCVersion(c.version.Int32), + ClientID: c.clientID.String, + RedirectURIs: c.redirectUris, + AppType: domain.OIDCApplicationType(c.applicationType.Int16), + AuthMethodType: domain.OIDCAuthMethodType(c.authMethodType.Int16), + PostLogoutRedirectURIs: c.postLogoutRedirectUris, + IsDevMode: c.devMode.Bool, + AccessTokenType: domain.OIDCTokenType(c.accessTokenType.Int16), + AssertAccessTokenRole: c.accessTokenRoleAssertion.Bool, + AssertIDTokenRole: c.iDTokenRoleAssertion.Bool, + AssertIDTokenUserinfo: c.iDTokenUserinfoAssertion.Bool, + ClockSkew: time.Duration(c.clockSkew.Int64), + AdditionalOrigins: c.additionalOrigins, + ResponseTypes: c.responseTypes, + GrantTypes: c.grantTypes, + SkipNativeAppSuccessPage: c.skipNativeAppSuccessPage.Bool, } compliance := domain.GetOIDCCompliance(app.OIDCConfig.Version, app.OIDCConfig.AppType, app.OIDCConfig.GrantTypes, app.OIDCConfig.ResponseTypes, app.OIDCConfig.AuthMethodType, app.OIDCConfig.RedirectURIs) app.OIDCConfig.ComplianceProblems = compliance.Problems diff --git a/internal/query/app_test.go b/internal/query/app_test.go index 3076a6ed0..e75e4ce06 100644 --- a/internal/query/app_test.go +++ b/internal/query/app_test.go @@ -15,96 +15,98 @@ import ( ) var ( - expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps4.id,` + - ` projections.apps4.name,` + - ` projections.apps4.project_id,` + - ` projections.apps4.creation_date,` + - ` projections.apps4.change_date,` + - ` projections.apps4.resource_owner,` + - ` projections.apps4.state,` + - ` projections.apps4.sequence,` + + expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` + + ` projections.apps5.name,` + + ` projections.apps5.project_id,` + + ` projections.apps5.creation_date,` + + ` projections.apps5.change_date,` + + ` projections.apps5.resource_owner,` + + ` projections.apps5.state,` + + ` projections.apps5.sequence,` + // api config - ` projections.apps4_api_configs.app_id,` + - ` projections.apps4_api_configs.client_id,` + - ` projections.apps4_api_configs.auth_method,` + + ` projections.apps5_api_configs.app_id,` + + ` projections.apps5_api_configs.client_id,` + + ` projections.apps5_api_configs.auth_method,` + // oidc config - ` projections.apps4_oidc_configs.app_id,` + - ` projections.apps4_oidc_configs.version,` + - ` projections.apps4_oidc_configs.client_id,` + - ` projections.apps4_oidc_configs.redirect_uris,` + - ` projections.apps4_oidc_configs.response_types,` + - ` projections.apps4_oidc_configs.grant_types,` + - ` projections.apps4_oidc_configs.application_type,` + - ` projections.apps4_oidc_configs.auth_method_type,` + - ` projections.apps4_oidc_configs.post_logout_redirect_uris,` + - ` projections.apps4_oidc_configs.is_dev_mode,` + - ` projections.apps4_oidc_configs.access_token_type,` + - ` projections.apps4_oidc_configs.access_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_userinfo_assertion,` + - ` projections.apps4_oidc_configs.clock_skew,` + - ` projections.apps4_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.app_id,` + + ` projections.apps5_oidc_configs.version,` + + ` projections.apps5_oidc_configs.client_id,` + + ` projections.apps5_oidc_configs.redirect_uris,` + + ` projections.apps5_oidc_configs.response_types,` + + ` projections.apps5_oidc_configs.grant_types,` + + ` projections.apps5_oidc_configs.application_type,` + + ` projections.apps5_oidc_configs.auth_method_type,` + + ` projections.apps5_oidc_configs.post_logout_redirect_uris,` + + ` projections.apps5_oidc_configs.is_dev_mode,` + + ` projections.apps5_oidc_configs.access_token_type,` + + ` projections.apps5_oidc_configs.access_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_userinfo_assertion,` + + ` projections.apps5_oidc_configs.clock_skew,` + + ` projections.apps5_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.skip_native_app_success_page,` + //saml config - ` projections.apps4_saml_configs.app_id,` + - ` projections.apps4_saml_configs.entity_id,` + - ` projections.apps4_saml_configs.metadata,` + - ` projections.apps4_saml_configs.metadata_url` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` projections.apps5_saml_configs.app_id,` + + ` projections.apps5_saml_configs.entity_id,` + + ` projections.apps5_saml_configs.metadata,` + + ` projections.apps5_saml_configs.metadata_url` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps4.id,` + - ` projections.apps4.name,` + - ` projections.apps4.project_id,` + - ` projections.apps4.creation_date,` + - ` projections.apps4.change_date,` + - ` projections.apps4.resource_owner,` + - ` projections.apps4.state,` + - ` projections.apps4.sequence,` + + expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` + + ` projections.apps5.name,` + + ` projections.apps5.project_id,` + + ` projections.apps5.creation_date,` + + ` projections.apps5.change_date,` + + ` projections.apps5.resource_owner,` + + ` projections.apps5.state,` + + ` projections.apps5.sequence,` + // api config - ` projections.apps4_api_configs.app_id,` + - ` projections.apps4_api_configs.client_id,` + - ` projections.apps4_api_configs.auth_method,` + + ` projections.apps5_api_configs.app_id,` + + ` projections.apps5_api_configs.client_id,` + + ` projections.apps5_api_configs.auth_method,` + // oidc config - ` projections.apps4_oidc_configs.app_id,` + - ` projections.apps4_oidc_configs.version,` + - ` projections.apps4_oidc_configs.client_id,` + - ` projections.apps4_oidc_configs.redirect_uris,` + - ` projections.apps4_oidc_configs.response_types,` + - ` projections.apps4_oidc_configs.grant_types,` + - ` projections.apps4_oidc_configs.application_type,` + - ` projections.apps4_oidc_configs.auth_method_type,` + - ` projections.apps4_oidc_configs.post_logout_redirect_uris,` + - ` projections.apps4_oidc_configs.is_dev_mode,` + - ` projections.apps4_oidc_configs.access_token_type,` + - ` projections.apps4_oidc_configs.access_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_userinfo_assertion,` + - ` projections.apps4_oidc_configs.clock_skew,` + - ` projections.apps4_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.app_id,` + + ` projections.apps5_oidc_configs.version,` + + ` projections.apps5_oidc_configs.client_id,` + + ` projections.apps5_oidc_configs.redirect_uris,` + + ` projections.apps5_oidc_configs.response_types,` + + ` projections.apps5_oidc_configs.grant_types,` + + ` projections.apps5_oidc_configs.application_type,` + + ` projections.apps5_oidc_configs.auth_method_type,` + + ` projections.apps5_oidc_configs.post_logout_redirect_uris,` + + ` projections.apps5_oidc_configs.is_dev_mode,` + + ` projections.apps5_oidc_configs.access_token_type,` + + ` projections.apps5_oidc_configs.access_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_userinfo_assertion,` + + ` projections.apps5_oidc_configs.clock_skew,` + + ` projections.apps5_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.skip_native_app_success_page,` + //saml config - ` projections.apps4_saml_configs.app_id,` + - ` projections.apps4_saml_configs.entity_id,` + - ` projections.apps4_saml_configs.metadata,` + - ` projections.apps4_saml_configs.metadata_url,` + + ` projections.apps5_saml_configs.app_id,` + + ` projections.apps5_saml_configs.entity_id,` + + ` projections.apps5_saml_configs.metadata,` + + ` projections.apps5_saml_configs.metadata_url,` + ` COUNT(*) OVER ()` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps4_api_configs.client_id,` + - ` projections.apps4_oidc_configs.client_id` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + + expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps5_api_configs.client_id,` + + ` projections.apps5_oidc_configs.client_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps4.project_id` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.project_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) expectedProjectByAppQuery = regexp.QuoteMeta(`SELECT projections.projects3.id,` + ` projections.projects3.creation_date,` + @@ -118,10 +120,10 @@ var ( ` projections.projects3.has_project_check,` + ` projections.projects3.private_labeling_setting` + ` FROM projections.projects3` + - ` JOIN projections.apps4 ON projections.projects3.id = projections.apps4.project_id AND projections.projects3.instance_id = projections.apps4.instance_id` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` JOIN projections.apps5 ON projections.projects3.id = projections.apps5.project_id AND projections.projects3.instance_id = projections.apps5.instance_id` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) appCols = database.StringArray{ @@ -154,6 +156,7 @@ var ( "id_token_userinfo_assertion", "clock_skew", "additional_origins", + "skip_native_app_success_page", //saml config "app_id", "entity_id", @@ -224,6 +227,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -289,6 +293,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -357,6 +362,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "app-id", "https://test.com/saml/metadata", @@ -427,6 +433,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -451,23 +458,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -511,6 +519,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -535,23 +544,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -595,6 +605,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -619,23 +630,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -679,6 +691,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -703,23 +716,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -763,6 +777,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -787,23 +802,110 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, + }, + }, + }, + }, + }, + { + name: "prepareAppsQuery oidc app native success page skip", + prepare: prepareAppsQuery, + want: want{ + sqlExpectations: mockQueries( + expectedAppsQuery, + appsCols, + [][]driver.Value{ + { + "app-id", + "app-name", + "project-id", + testNow, + testNow, + "ro", + domain.AppStateActive, + uint64(20211109), + // api config + nil, + nil, + nil, + // oidc config + "app-id", + domain.OIDCVersionV1, + "oidc-client-id", + database.StringArray{"https://redirect.to/me"}, + database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + domain.OIDCApplicationTypeNative, + domain.OIDCAuthMethodTypeNone, + database.StringArray{"post.logout.ch"}, + false, + domain.OIDCTokenTypeJWT, + false, + false, + true, + 1 * time.Second, + database.StringArray{"additional.origin"}, + true, + // saml config + nil, + nil, + nil, + nil, + }, + }, + ), + }, + object: &Apps{ + SearchResponse: SearchResponse{ + Count: 1, + }, + Apps: []*App{ + { + ID: "app-id", + CreationDate: testNow, + ChangeDate: testNow, + ResourceOwner: "ro", + State: domain.AppStateActive, + Sequence: 20211109, + Name: "app-name", + ProjectID: "project-id", + OIDCConfig: &OIDCApp{ + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeNative, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: true, }, }, }, @@ -847,6 +949,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -883,6 +986,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -919,6 +1023,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "saml-app-id", "https://test.com/saml/metadata", @@ -943,23 +1048,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, { @@ -1085,6 +1191,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -1142,6 +1249,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -1204,6 +1312,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1223,23 +1332,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, { @@ -1280,6 +1390,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "app-id", "https://test.com/saml/metadata", @@ -1343,6 +1454,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1362,23 +1474,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1420,6 +1533,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1439,23 +1553,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1497,6 +1612,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1516,23 +1632,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1574,6 +1691,7 @@ func Test_AppPrepare(t *testing.T) { false, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1593,23 +1711,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: false, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: false, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, diff --git a/internal/query/projection/app.go b/internal/query/projection/app.go index ee5b34fbc..483764d5d 100644 --- a/internal/query/projection/app.go +++ b/internal/query/projection/app.go @@ -15,7 +15,7 @@ import ( ) const ( - AppProjectionTable = "projections.apps4" + AppProjectionTable = "projections.apps5" AppAPITable = AppProjectionTable + "_" + appAPITableSuffix AppOIDCTable = AppProjectionTable + "_" + appOIDCTableSuffix AppSAMLTable = AppProjectionTable + "_" + appSAMLTableSuffix @@ -57,6 +57,7 @@ const ( AppOIDCConfigColumnIDTokenUserinfoAssertion = "id_token_userinfo_assertion" AppOIDCConfigColumnClockSkew = "clock_skew" AppOIDCConfigColumnAdditionalOrigins = "additional_origins" + AppOIDCConfigColumnSkipNativeAppSuccessPage = "skip_native_app_success_page" appSAMLTableSuffix = "saml_configs" AppSAMLConfigColumnAppID = "app_id" @@ -122,6 +123,7 @@ func newAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) * crdb.NewColumn(AppOIDCConfigColumnIDTokenUserinfoAssertion, crdb.ColumnTypeBool, crdb.Default(false)), crdb.NewColumn(AppOIDCConfigColumnClockSkew, crdb.ColumnTypeInt64, crdb.Default(0)), crdb.NewColumn(AppOIDCConfigColumnAdditionalOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()), + crdb.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, crdb.ColumnTypeBool, crdb.Default(false)), }, crdb.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID), appOIDCTableSuffix, @@ -463,6 +465,7 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. handler.NewCol(AppOIDCConfigColumnIDTokenUserinfoAssertion, e.IDTokenUserinfoAssertion), handler.NewCol(AppOIDCConfigColumnClockSkew, e.ClockSkew), handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(e.AdditionalOrigins)), + handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, e.SkipNativeAppSuccessPage), }, crdb.WithTableSuffix(appOIDCTableSuffix), ), @@ -528,6 +531,9 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle if e.AdditionalOrigins != nil { cols = append(cols, handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(*e.AdditionalOrigins))) } + if e.SkipNativeAppSuccessPage != nil { + cols = append(cols, handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, *e.SkipNativeAppSuccessPage)) + } if len(cols) == 0 { return crdb.NewNoOpStatement(e), nil diff --git a/internal/query/projection/app_test.go b/internal/query/projection/app_test.go index 9320b62ad..c605ed6dc 100644 --- a/internal/query/projection/app_test.go +++ b/internal/query/projection/app_test.go @@ -45,7 +45,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.apps5 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "app-id", "my-app", @@ -82,7 +82,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ "my-app", anyArg{}, @@ -95,6 +95,27 @@ func TestAppProjection_reduces(t *testing.T) { }, }, }, + { + name: "project reduceAppChanged no change", + args: args{ + event: getEvent(testEvent( + repository.EventType(project.ApplicationChangedType), + project.AggregateType, + []byte(`{ + "appId": "app-id" + }`), + ), project.ApplicationChangedEventMapper), + }, + reduce: (&appProjection{}).reduceAppChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{}, + }, + }, + }, { name: "project reduceAppDeactivated", args: args{ @@ -114,7 +135,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateInactive, anyArg{}, @@ -146,7 +167,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateActive, anyArg{}, @@ -178,7 +199,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -205,7 +226,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (project_id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (project_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -232,7 +253,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -263,7 +284,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.apps5_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -273,7 +294,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -307,7 +328,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, domain.APIAuthMethodTypePrivateKeyJWT, @@ -316,7 +337,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -369,7 +390,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.apps5_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", @@ -377,7 +398,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -412,7 +433,8 @@ func TestAppProjection_reduces(t *testing.T) { "idTokenRoleAssertion": true, "idTokenUserinfoAssertion": true, "clockSkew": 1000, - "additionalOrigins": ["origin.one.ch", "origin.two.ch"] + "additionalOrigins": ["origin.one.ch", "origin.two.ch"], + "skipNativeAppSuccessPage": true }`), ), project.OIDCConfigAddedEventMapper), }, @@ -424,7 +446,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)", + expectedStmt: "INSERT INTO projections.apps5_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -444,10 +466,11 @@ func TestAppProjection_reduces(t *testing.T) { true, 1 * time.Microsecond, database.StringArray{"origin.one.ch", "origin.two.ch"}, + true, }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -480,7 +503,9 @@ func TestAppProjection_reduces(t *testing.T) { "idTokenRoleAssertion": true, "idTokenUserinfoAssertion": true, "clockSkew": 1000, - "additionalOrigins": ["origin.one.ch", "origin.two.ch"] + "additionalOrigins": ["origin.one.ch", "origin.two.ch"], + "skipNativeAppSuccessPage": true + }`), ), project.OIDCConfigChangedEventMapper), }, @@ -492,7 +517,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) WHERE (app_id = $15) AND (instance_id = $16)", + expectedStmt: "UPDATE projections.apps5_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)", expectedArgs: []interface{}{ domain.OIDCVersionV1, database.StringArray{"redirect.one.ch", "redirect.two.ch"}, @@ -508,12 +533,13 @@ func TestAppProjection_reduces(t *testing.T) { true, 1 * time.Microsecond, database.StringArray{"origin.one.ch", "origin.two.ch"}, + true, "app-id", "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -566,7 +592,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.apps5_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", @@ -574,7 +600,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -603,7 +629,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), diff --git a/internal/repository/project/oidc_config.go b/internal/repository/project/oidc_config.go index df809daa7..d39e92c2a 100644 --- a/internal/repository/project/oidc_config.go +++ b/internal/repository/project/oidc_config.go @@ -40,6 +40,7 @@ type OIDCConfigAddedEvent struct { IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion,omitempty"` ClockSkew time.Duration `json:"clockSkew,omitempty"` AdditionalOrigins []string `json:"additionalOrigins,omitempty"` + SkipNativeAppSuccessPage bool `json:"skipNativeAppSuccessPage,omitempty"` } func (e *OIDCConfigAddedEvent) Data() interface{} { @@ -70,6 +71,7 @@ func NewOIDCConfigAddedEvent( idTokenUserinfoAssertion bool, clockSkew time.Duration, additionalOrigins []string, + skipNativeAppSuccessPage bool, ) *OIDCConfigAddedEvent { return &OIDCConfigAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -94,6 +96,7 @@ func NewOIDCConfigAddedEvent( IDTokenUserinfoAssertion: idTokenUserinfoAssertion, ClockSkew: clockSkew, AdditionalOrigins: additionalOrigins, + SkipNativeAppSuccessPage: skipNativeAppSuccessPage, } } @@ -179,8 +182,7 @@ func (e *OIDCConfigAddedEvent) Validate(cmd eventstore.Command) bool { return false } } - - return true + return e.SkipNativeAppSuccessPage == c.SkipNativeAppSuccessPage } func OIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { @@ -214,6 +216,7 @@ type OIDCConfigChangedEvent struct { IDTokenUserinfoAssertion *bool `json:"idTokenUserinfoAssertion,omitempty"` ClockSkew *time.Duration `json:"clockSkew,omitempty"` AdditionalOrigins *[]string `json:"additionalOrigins,omitempty"` + SkipNativeAppSuccessPage *bool `json:"skipNativeAppSuccessPage,omitempty"` } func (e *OIDCConfigChangedEvent) Data() interface{} { @@ -334,6 +337,12 @@ func ChangeAdditionalOrigins(additionalOrigins []string) func(event *OIDCConfigC } } +func ChangeSkipNativeAppSuccessPage(skipNativeAppSuccessPage bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.SkipNativeAppSuccessPage = &skipNativeAppSuccessPage + } +} + func OIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { e := &OIDCConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), diff --git a/proto/zitadel/app.proto b/proto/zitadel/app.proto index 0a855f955..d889135bc 100644 --- a/proto/zitadel/app.proto +++ b/proto/zitadel/app.proto @@ -163,6 +163,11 @@ message OIDCConfig { description: "all allowed origins from where the API can be used"; } ]; + bool skip_native_app_success_page = 20 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } enum OIDCResponseType { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 71dd9b99f..e410d839a 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -8773,6 +8773,11 @@ message AddOIDCAppRequest { description: "Additional origins (other than the redirect_uris) from where the API can be used"; } ]; + bool skip_native_app_success_page = 17 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } message AddOIDCAppResponse { @@ -8943,6 +8948,11 @@ message UpdateOIDCAppConfigRequest { description: "Additional origins (other than the redirect_uris) from where the API can be used"; } ]; + bool skip_native_app_success_page = 16 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } message UpdateOIDCAppConfigResponse { From a6a46796c8c58c5c12a0f31b7b1a79fa7e06dba4 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 14 Apr 2023 09:04:18 +0200 Subject: [PATCH 02/76] build next --- .releaserc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.releaserc.js b/.releaserc.js index 250d6d71d..ed7f41295 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,6 +1,7 @@ module.exports = { branches: [ {name: 'main'}, + {name: 'next'}, {name: '1.87.x', range: '1.87.x', channel: '1.87.x'} ], plugins: [ From d72b7b06178efcd384f74510df3dedf2c6822e3b Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 19 Apr 2023 11:51:03 +0200 Subject: [PATCH 03/76] fix(console): disallow inline fonts, critical styles (#5714) fix: disallow inline --- console/angular.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/console/angular.json b/console/angular.json index ec2bab784..88d43181d 100644 --- a/console/angular.json +++ b/console/angular.json @@ -40,6 +40,14 @@ }, "configurations": { "production": { + "optimization": { + "fonts": { + "inline": false + }, + "styles": { + "inlineCritical": false + } + }, "budgets": [ { "type": "initial", From 08236b0d1286f6195c5541ba5d33a146a416d543 Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 20 Apr 2023 07:31:18 +0200 Subject: [PATCH 04/76] fix(setup): step 10 for postgres (#5717) --- cmd/setup/10.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/setup/10.sql b/cmd/setup/10.sql index ae2af741d..27853f55a 100644 --- a/cmd/setup/10.sql +++ b/cmd/setup/10.sql @@ -1,6 +1,6 @@ CREATE temporary TABLE IF NOT EXISTS wrong_events ( - instance_id STRING - , event_sequence INT8 + instance_id TEXT + , event_sequence BIGINT , current_cd TIMESTAMPTZ , next_cd TIMESTAMPTZ ); @@ -19,7 +19,7 @@ INSERT INTO wrong_events ( ) AS next_cd FROM eventstore.events - ) WHERE + ) sub WHERE current_cd < next_cd ORDER BY event_sequence DESC From 6bce14edadf4a91b56b431193ff9aab15b2b861e Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 25 Apr 2023 09:48:32 +0200 Subject: [PATCH 05/76] fix(setup): smaller transactions (#5743) --- cmd/setup/10.go | 48 ++++++++++++++++++++++---------------------- cmd/setup/10.sql | 2 -- cmd/setup/config.go | 20 +++++++++--------- cmd/setup/setup.go | 4 ++-- cmd/setup/steps.yaml | 2 ++ 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/cmd/setup/10.go b/cmd/setup/10.go index 77db7c6c5..4efccc653 100644 --- a/cmd/setup/10.go +++ b/cmd/setup/10.go @@ -2,8 +2,11 @@ package setup import ( "context" + "database/sql" _ "embed" + "time" + "github.com/cockroachdb/cockroach-go/v2/crdb" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/database" @@ -15,35 +18,32 @@ var ( ) type CorrectCreationDate struct { - dbClient *database.DB + dbClient *database.DB + FailAfter time.Duration } func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { - tx, err := mig.dbClient.Begin() - if err != nil { - return err - } - if mig.dbClient.Type() == "cockroach" { - if _, err := tx.Exec("SET experimental_enable_temp_tables=on"); err != nil { - return err - } - } - defer func() { - if err != nil { - logging.OnError(tx.Rollback()).Debug("rollback failed") - return - } - err = tx.Commit() - }() + ctx, cancel := context.WithTimeout(ctx, mig.FailAfter) + defer cancel() + for { - res, err := tx.ExecContext(ctx, correctCreationDate10) - if err != nil { - return err - } - affected, _ := res.RowsAffected() - logging.WithFields("count", affected).Info("creation dates changed") - if affected == 0 { + var affected int64 + err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { + if mig.dbClient.Type() == "cockroach" { + if _, err := tx.Exec("SET experimental_enable_temp_tables=on"); err != nil { + return err + } + } + res, err := tx.ExecContext(ctx, correctCreationDate10) + if err != nil { + return err + } + affected, _ = res.RowsAffected() + logging.WithFields("count", affected).Info("creation dates changed") return nil + }) + if affected == 0 || err != nil { + return err } } } diff --git a/cmd/setup/10.sql b/cmd/setup/10.sql index 27853f55a..591d0b3d8 100644 --- a/cmd/setup/10.sql +++ b/cmd/setup/10.sql @@ -5,8 +5,6 @@ CREATE temporary TABLE IF NOT EXISTS wrong_events ( , next_cd TIMESTAMPTZ ); -TRUNCATE wrong_events; - INSERT INTO wrong_events ( SELECT * FROM ( SELECT diff --git a/cmd/setup/config.go b/cmd/setup/config.go index eb55ba492..dc75d5404 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -56,16 +56,16 @@ func MustNewConfig(v *viper.Viper) *Config { } type Steps struct { - s1ProjectionTable *ProjectionTable - s2AssetsTable *AssetTable - FirstInstance *FirstInstance - s4EventstoreIndexes *EventstoreIndexesNew - s5LastFailed *LastFailed - s6OwnerRemoveColumns *OwnerRemoveColumns - s7LogstoreTables *LogstoreTables - s8AuthTokens *AuthTokenIndexes - s9EventstoreIndexes2 *EventstoreIndexesNew - s10EventstoreCreationDate *CorrectCreationDate + s1ProjectionTable *ProjectionTable + s2AssetsTable *AssetTable + FirstInstance *FirstInstance + s4EventstoreIndexes *EventstoreIndexesNew + s5LastFailed *LastFailed + s6OwnerRemoveColumns *OwnerRemoveColumns + s7LogstoreTables *LogstoreTables + s8AuthTokens *AuthTokenIndexes + s9EventstoreIndexes2 *EventstoreIndexesNew + CorrectCreationDate *CorrectCreationDate } type encryptionKeyConfig struct { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index a7deef007..e90ad85f9 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -88,7 +88,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s7LogstoreTables = &LogstoreTables{dbClient: dbClient.DB, username: config.Database.Username(), dbType: config.Database.Type()} steps.s8AuthTokens = &AuthTokenIndexes{dbClient: dbClient} steps.s9EventstoreIndexes2 = New09(dbClient) - steps.s10EventstoreCreationDate = &CorrectCreationDate{dbClient: dbClient} + steps.CorrectCreationDate.dbClient = dbClient err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -124,7 +124,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { logging.OnError(err).Fatal("unable to migrate step 8") err = migration.Migrate(ctx, eventstoreClient, steps.s9EventstoreIndexes2) logging.OnError(err).Fatal("unable to migrate step 9") - err = migration.Migrate(ctx, eventstoreClient, steps.s10EventstoreCreationDate) + err = migration.Migrate(ctx, eventstoreClient, steps.CorrectCreationDate) logging.OnError(err).Fatal("unable to migrate step 10") for _, repeatableStep := range repeatableSteps { diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index c362f80e8..f5cb4b0c2 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -30,3 +30,5 @@ FirstInstance: MachineKey: ExpirationDate: Type: +CorrectCreationDate: + FailAfter: 5m From 185a4e8c4736f3210efa733184e07f21308e0ad0 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Thu, 11 May 2023 09:33:47 +0200 Subject: [PATCH 06/76] fix: use correct org id for external authentication actions (#5793) (#5816) --- internal/api/ui/login/custom_action.go | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/internal/api/ui/login/custom_action.go b/internal/api/ui/login/custom_action.go index 772017277..d6dbe6cdd 100644 --- a/internal/api/ui/login/custom_action.go +++ b/internal/api/ui/login/custom_action.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/dop251/goja" + "github.com/zitadel/logging" "github.com/zitadel/oidc/v2/pkg/oidc" "golang.org/x/text/language" @@ -14,6 +15,7 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/idp" + "github.com/zitadel/zitadel/internal/query" ) func (l *Login) runPostExternalAuthenticationActions( @@ -26,7 +28,21 @@ func (l *Login) runPostExternalAuthenticationActions( ) (_ *domain.ExternalUser, userChanged bool, err error) { ctx := httpRequest.Context() + // use the request org (scopes or domain discovery) as default resourceOwner := authRequest.RequestedOrgID + // if the user was already linked to an IDP and redirected to that, the requested org might be empty + if resourceOwner == "" { + resourceOwner = authRequest.UserOrgID + } + // if we will have no org (e.g. user clicked directly on the IDP on the login page) + if resourceOwner == "" { + // in this case the user might nevertheless already be linked to an IDP, + // so let's do a workaround and resourceOwnerOfUserIDPLink if there would be a IDP link + resourceOwner, err = l.resourceOwnerOfUserIDPLink(ctx, authRequest.SelectedIDPConfigID, user.ExternalUserID) + logging.WithFields("authReq", authRequest.ID, "idpID", authRequest.SelectedIDPConfigID).OnError(err). + Warn("could not determine resource owner for runPostExternalAuthenticationActions, fall back to default org id") + } + // fallback to default org id if resourceOwner == "" { resourceOwner = authz.GetInstance(ctx).DefaultOrganisationID() } @@ -394,3 +410,25 @@ func tokenCtxFields(tokens *oidc.Tokens[*oidc.IDTokenClaims]) []actions.FieldOpt actions.SetFields("claimsJSON", claimsJSON), } } + +func (l *Login) resourceOwnerOfUserIDPLink(ctx context.Context, idpConfigID string, externalUserID string) (string, error) { + idQuery, err := query.NewIDPUserLinkIDPIDSearchQuery(idpConfigID) + if err != nil { + return "", err + } + externalIDQuery, err := query.NewIDPUserLinksExternalIDSearchQuery(externalUserID) + if err != nil { + return "", err + } + queries := []query.SearchQuery{ + idQuery, externalIDQuery, + } + links, err := l.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false) + if err != nil { + return "", err + } + if len(links.Links) != 1 { + return "", nil + } + return links.Links[0].ResourceOwner, nil +} From 32d33d3145c0c810e0d90f2e323ea2830298834b Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 11 May 2023 18:02:34 +0200 Subject: [PATCH 07/76] fix: render authrequest id only if possible (#5823) --- internal/api/ui/login/renderer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/api/ui/login/renderer.go b/internal/api/ui/login/renderer.go index a9b12f19d..5c20168d3 100644 --- a/internal/api/ui/login/renderer.go +++ b/internal/api/ui/login/renderer.go @@ -327,7 +327,12 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq * func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) { var msg string if err != nil { - logging.WithError(err).WithField("auth_req_id", authReq.ID).Error() + log := logging.WithError(err) + if authReq != nil { + log = log.WithField("auth_req_id", authReq.ID) + } + log.Error() + _, msg = l.getErrorMessage(r, err) } data := l.getBaseData(r, authReq, "Errors.Internal", "", "Internal", msg) From f41f9a0b0227d2a8bdf84ee0164e1b072f07db54 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Thu, 25 May 2023 08:32:51 +0200 Subject: [PATCH 08/76] chore: rc as prerelease --- .releaserc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.releaserc.js b/.releaserc.js index 7ccabf26d..2103f3f3d 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -2,6 +2,7 @@ module.exports = { branches: [ { name: 'main' }, { name: 'next' }, + { name: 'rc', prerelease: true }, ], plugins: [ "@semantic-release/commit-analyzer" From 57e189e590f2ea4c614dfa71f6b495d0544e5592 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Fri, 26 May 2023 09:24:52 +0200 Subject: [PATCH 09/76] fix: token for post authentication action and change phone and email (#5933) * fix: token for post authentication action and change phone and email * fix checks and add tests * improve change checks and add tests * add more tests * remove unintended test --------- Co-authored-by: Livio Spring --- .../api/ui/login/external_provider_handler.go | 144 ++++++--- .../login/external_provider_handler_test.go | 286 ++++++++++++++++++ internal/command/user_human_email.go | 10 +- internal/command/user_human_email_test.go | 54 ++++ internal/command/user_human_phone.go | 10 +- internal/command/user_human_phone_test.go | 60 ++++ 6 files changed, 514 insertions(+), 50 deletions(-) create mode 100644 internal/api/ui/login/external_provider_handler_test.go diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index 0012d1f3e..a5a365ea5 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -558,54 +558,106 @@ func (l *Login) updateExternalUser(ctx context.Context, authReq *domain.AuthRequ if user.Human == nil { return errors.ThrowPreconditionFailed(nil, "LOGIN-WLTce", "Errors.User.NotHuman") } - if externalUser.Email != "" && (externalUser.Email != user.Human.Email || externalUser.IsEmailVerified != user.Human.IsEmailVerified) { - emailCodeGenerator, err := l.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg) - logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update email") - if err == nil { - _, err = l.command.ChangeHumanEmail(setContext(ctx, authReq.UserOrgID), - &domain.Email{ - ObjectRoot: models.ObjectRoot{AggregateID: authReq.UserID}, - EmailAddress: externalUser.Email, - IsEmailVerified: externalUser.IsEmailVerified, - }, - emailCodeGenerator) - logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update email") - } - } - if externalUser.Phone != "" && (externalUser.Phone != user.Human.Phone || externalUser.IsPhoneVerified != user.Human.IsPhoneVerified) { - phoneCodeGenerator, err := l.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg) - logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update phone") - if err == nil { - _, err = l.command.ChangeHumanPhone(setContext(ctx, authReq.UserOrgID), - &domain.Phone{ - ObjectRoot: models.ObjectRoot{AggregateID: authReq.UserID}, - PhoneNumber: externalUser.Phone, - IsPhoneVerified: externalUser.IsPhoneVerified, - }, - authReq.UserOrgID, - phoneCodeGenerator) - logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update phone") - } - } - if externalUser.FirstName != user.Human.FirstName || - externalUser.LastName != user.Human.LastName || - externalUser.NickName != user.Human.NickName || - externalUser.DisplayName != user.Human.DisplayName || - externalUser.PreferredLanguage != user.Human.PreferredLanguage { - _, err = l.command.ChangeHumanProfile(setContext(ctx, authReq.UserOrgID), &domain.Profile{ - ObjectRoot: models.ObjectRoot{AggregateID: authReq.UserID}, - FirstName: externalUser.FirstName, - LastName: externalUser.LastName, - NickName: externalUser.NickName, - DisplayName: externalUser.DisplayName, - PreferredLanguage: externalUser.PreferredLanguage, - Gender: user.Human.Gender, - }) - logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update profile") - } + err = l.updateExternalUserEmail(ctx, user, externalUser) + logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update email") + + err = l.updateExternalUserPhone(ctx, user, externalUser) + logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update phone") + + err = l.updateExternalUserProfile(ctx, user, externalUser) + logging.WithFields("authReq", authReq.ID, "user", authReq.UserID).OnError(err).Error("unable to update profile") + return nil } +func (l *Login) updateExternalUserEmail(ctx context.Context, user *query.User, externalUser *domain.ExternalUser) error { + changed := hasEmailChanged(user, externalUser) + if !changed { + return nil + } + // if the email has changed and / or was not verified, we change it + emailCodeGenerator, err := l.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg) + if err != nil { + return err + } + _, err = l.command.ChangeHumanEmail(setContext(ctx, user.ResourceOwner), + &domain.Email{ + ObjectRoot: models.ObjectRoot{AggregateID: user.ID}, + EmailAddress: externalUser.Email, + IsEmailVerified: externalUser.IsEmailVerified, + }, + emailCodeGenerator) + return err +} + +func (l *Login) updateExternalUserPhone(ctx context.Context, user *query.User, externalUser *domain.ExternalUser) error { + changed, err := hasPhoneChanged(user, externalUser) + if !changed || err != nil { + return err + } + // if the phone has changed and / or was not verified, we change it + phoneCodeGenerator, err := l.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg) + if err != nil { + return err + } + _, err = l.command.ChangeHumanPhone(setContext(ctx, user.ResourceOwner), + &domain.Phone{ + ObjectRoot: models.ObjectRoot{AggregateID: user.ID}, + PhoneNumber: externalUser.Phone, + IsPhoneVerified: externalUser.IsPhoneVerified, + }, + user.ResourceOwner, + phoneCodeGenerator) + return err +} + +func (l *Login) updateExternalUserProfile(ctx context.Context, user *query.User, externalUser *domain.ExternalUser) error { + if externalUser.FirstName == user.Human.FirstName && + externalUser.LastName == user.Human.LastName && + externalUser.NickName == user.Human.NickName && + externalUser.DisplayName == user.Human.DisplayName && + externalUser.PreferredLanguage == user.Human.PreferredLanguage { + return nil + } + _, err := l.command.ChangeHumanProfile(setContext(ctx, user.ResourceOwner), &domain.Profile{ + ObjectRoot: models.ObjectRoot{AggregateID: user.ID}, + FirstName: externalUser.FirstName, + LastName: externalUser.LastName, + NickName: externalUser.NickName, + DisplayName: externalUser.DisplayName, + PreferredLanguage: externalUser.PreferredLanguage, + Gender: user.Human.Gender, + }) + return err +} + +func hasEmailChanged(user *query.User, externalUser *domain.ExternalUser) bool { + externalUser.Email = externalUser.Email.Normalize() + if externalUser.Email == "" { + return false + } + // ignore if the same email is not set to verified anymore + if externalUser.Email == user.Human.Email && user.Human.IsEmailVerified { + return false + } + return externalUser.Email != user.Human.Email || externalUser.IsEmailVerified != user.Human.IsEmailVerified +} + +func hasPhoneChanged(user *query.User, externalUser *domain.ExternalUser) (_ bool, err error) { + if externalUser.Phone == "" { + return false, nil + } + externalUser.Phone, err = externalUser.Phone.Normalize() + if err != nil { + return false, err + } + // ignore if the same phone is not set to verified anymore + if externalUser.Phone == user.Human.Phone && user.Human.IsPhoneVerified { + return false, nil + } + return externalUser.Phone != user.Human.Phone || externalUser.IsPhoneVerified != user.Human.IsPhoneVerified, nil +} + func (l *Login) ldapProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*ldap.Provider, error) { password, err := crypto.DecryptString(identityProvider.LDAPIDPTemplate.BindPassword, l.idpConfigAlg) if err != nil { @@ -851,6 +903,8 @@ func tokens(session idp.Session) *oidc.Tokens[*oidc.IDTokenClaims] { return s.Tokens case *jwt.Session: return s.Tokens + case *oauth.Session: + return s.Tokens } return nil } diff --git a/internal/api/ui/login/external_provider_handler_test.go b/internal/api/ui/login/external_provider_handler_test.go new file mode 100644 index 000000000..b1724fc35 --- /dev/null +++ b/internal/api/ui/login/external_provider_handler_test.go @@ -0,0 +1,286 @@ +package login + +import ( + "testing" + + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/query" +) + +func Test_hasEmailChanged(t *testing.T) { + type args struct { + user *query.User + externalUser *domain.ExternalUser + } + tests := []struct { + name string + args args + want bool + }{ + { + "no external mail", + args{ + user: &query.User{}, + externalUser: &domain.ExternalUser{}, + }, + false, + }, + { + "same email unverified", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("email@test.com"), + }, + }, + false, + }, + { + "same email verified", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + IsEmailVerified: true, + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("email@test.com"), + IsEmailVerified: true, + }, + }, + false, + }, + { + "email already verified", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + IsEmailVerified: true, + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("email@test.com"), + }, + }, + false, + }, + { + "email changed to verified", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("email@test.com"), + IsEmailVerified: true, + }, + }, + true, + }, + { + "email changed", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("new-email@test.com"), + }, + }, + true, + }, + { + "email changed and verified", + args{ + user: &query.User{ + Human: &query.Human{ + Email: domain.EmailAddress("email@test.com"), + IsEmailVerified: false, + }, + }, + externalUser: &domain.ExternalUser{ + Email: domain.EmailAddress("new-email@test.com"), + IsEmailVerified: true, + }, + }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hasEmailChanged(tt.args.user, tt.args.externalUser); got != tt.want { + t.Errorf("hasEmailChanged() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_hasPhoneChanged(t *testing.T) { + type args struct { + user *query.User + externalUser *domain.ExternalUser + } + tests := []struct { + name string + args args + want bool + wantErr bool + }{ + { + "no external phone", + args{ + user: &query.User{}, + externalUser: &domain.ExternalUser{}, + }, + false, + false, + }, + { + "invalid phone", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("invalid"), + }, + }, + false, + true, + }, + { + "same phone unverified", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + false, + false, + }, + { + "same phone verified", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + IsPhoneVerified: true, + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+41791234567"), + IsPhoneVerified: true, + }, + }, + false, + false, + }, + { + "phone already verified", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + IsPhoneVerified: true, + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + false, + false, + }, + { + "phone changed to verified", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+41791234567"), + IsPhoneVerified: true, + }, + }, + true, + false, + }, + { + "phone changed", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+4179654321"), + }, + }, + true, + false, + }, + { + "phone changed", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("+4179654321"), + IsPhoneVerified: true, + }, + }, + true, + false, + }, + { + "normalized phone unchanged", + args{ + user: &query.User{ + Human: &query.Human{ + Phone: domain.PhoneNumber("+41791234567"), + }, + }, + externalUser: &domain.ExternalUser{ + Phone: domain.PhoneNumber("0791234567"), + }, + }, + false, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := hasPhoneChanged(tt.args.user, tt.args.externalUser) + if (err != nil) != tt.wantErr { + t.Errorf("hasPhoneChanged() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("hasPhoneChanged() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/command/user_human_email.go b/internal/command/user_human_email.go index 66a498286..cdc737a00 100644 --- a/internal/command/user_human_email.go +++ b/internal/command/user_human_email.go @@ -33,12 +33,16 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, em } userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel) changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, userAgg, email.EmailAddress) - if !hasChanged { + + // only continue if there were changes or there were no changes and the email should be set to verified + if !hasChanged && !(email.IsEmailVerified && existingEmail.IsEmailVerified != email.IsEmailVerified) { return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2b7fM", "Errors.User.Email.NotChanged") } - events := []eventstore.Command{changedEvent} - + events := make([]eventstore.Command, 0) + if hasChanged { + events = append(events, changedEvent) + } if email.IsEmailVerified { events = append(events, user.NewHumanEmailVerifiedEvent(ctx, userAgg)) } else { diff --git a/internal/command/user_human_email_test.go b/internal/command/user_human_email_test.go index 45b63a3cd..3f41b2b5a 100644 --- a/internal/command/user_human_email_test.go +++ b/internal/command/user_human_email_test.go @@ -219,6 +219,60 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { }, }, }, + { + name: "email verified, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusher( + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + }, + ), + ), + }, + args: args{ + ctx: context.Background(), + email: &domain.Email{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + }, + EmailAddress: "email@test.ch", + IsEmailVerified: true, + }, + resourceOwner: "org1", + }, + res: res{ + want: &domain.Email{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + ResourceOwner: "org1", + }, + EmailAddress: "email@test.ch", + IsEmailVerified: true, + }, + }, + }, { name: "email changed with code, ok", fields: fields{ diff --git a/internal/command/user_human_phone.go b/internal/command/user_human_phone.go index e54880769..525333957 100644 --- a/internal/command/user_human_phone.go +++ b/internal/command/user_human_phone.go @@ -6,6 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -27,11 +28,16 @@ func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, re userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel) changedEvent, hasChanged := existingPhone.NewChangedEvent(ctx, userAgg, phone.PhoneNumber) - if !hasChanged { + + // only continue if there were changes or there were no changes and the phone should be set to verified + if !hasChanged && !(phone.IsPhoneVerified && existingPhone.IsPhoneVerified != phone.IsPhoneVerified) { return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-wF94r", "Errors.User.Phone.NotChanged") } - events := []eventstore.Command{changedEvent} + events := make([]eventstore.Command, 0) + if hasChanged { + events = append(events, changedEvent) + } if phone.IsPhoneVerified { events = append(events, user.NewHumanPhoneVerifiedEvent(ctx, userAgg)) } else { diff --git a/internal/command/user_human_phone_test.go b/internal/command/user_human_phone_test.go index 4d4480803..c7f7535a6 100644 --- a/internal/command/user_human_phone_test.go +++ b/internal/command/user_human_phone_test.go @@ -188,6 +188,66 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) { }, }, }, + { + name: "phone changed to verified, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + eventFromEventPusher( + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41711234567", + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusher( + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + }, + ), + ), + }, + args: args{ + ctx: context.Background(), + email: &domain.Phone{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + }, + PhoneNumber: "+41711234567", + IsPhoneVerified: true, + }, + resourceOwner: "org1", + }, + res: res{ + want: &domain.Phone{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + ResourceOwner: "org1", + }, + PhoneNumber: "+41711234567", + IsPhoneVerified: true, + }, + }, + }, { name: "phone changed with code, ok", fields: fields{ From 743c77a72943ca7033202f9a44de9f4d893061e2 Mon Sep 17 00:00:00 2001 From: Silvan Date: Fri, 2 Jun 2023 12:04:03 +0200 Subject: [PATCH 10/76] fix(setup): update chunks in step 11 (#5965) (#5966) --- cmd/setup/11.go | 66 +++++++++++++++++++++++++++++++- cmd/setup/11.sql | 15 -------- cmd/setup/11/11_add_column.sql | 6 +++ cmd/setup/11/11_fetch_events.sql | 1 + cmd/setup/11/11_fill_column.sql | 1 + cmd/setup/11/11_set_column.sql | 3 ++ 6 files changed, 76 insertions(+), 16 deletions(-) delete mode 100644 cmd/setup/11.sql create mode 100644 cmd/setup/11/11_add_column.sql create mode 100644 cmd/setup/11/11_fetch_events.sql create mode 100644 cmd/setup/11/11_fill_column.sql create mode 100644 cmd/setup/11/11_set_column.sql diff --git a/cmd/setup/11.go b/cmd/setup/11.go index 4745c7199..4126216da 100644 --- a/cmd/setup/11.go +++ b/cmd/setup/11.go @@ -2,14 +2,25 @@ package setup import ( "context" + "database/sql" _ "embed" + "time" + + "github.com/cockroachdb/cockroach-go/v2/crdb" + "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/database" ) var ( - //go:embed 11.sql + //go:embed 11/11_add_column.sql addEventCreatedAt string + //go:embed 11/11_fetch_events.sql + fetchCreatedAt string + //go:embed 11/11_fill_column.sql + fillCreatedAt string + //go:embed 11/11_set_column.sql + setCreatedAtDetails string ) type AddEventCreatedAt struct { @@ -24,6 +35,59 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { return err } _, err := mig.dbClient.ExecContext(ctx, addEventCreatedAt) + if err != nil { + return err + } + + for { + var count int + err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { + rows, err := tx.Query(fetchCreatedAt) + if err != nil { + return err + } + defer rows.Close() + + data := make(map[string]time.Time, 20) + for rows.Next() { + count++ + var ( + id string + creationDate time.Time + ) + + err = rows.Scan(&id, &creationDate) + if err != nil { + return err + } + + data[id] = creationDate + + } + if err := rows.Err(); err != nil { + return err + } + + for id, creationDate := range data { + _, err = tx.Exec(fillCreatedAt, creationDate, id) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + logging.WithFields("count", count).Debug("creation dates set") + if count < 20 { + break + } + } + + logging.Info("set details") + _, err = mig.dbClient.ExecContext(ctx, setCreatedAtDetails) return err } diff --git a/cmd/setup/11.sql b/cmd/setup/11.sql deleted file mode 100644 index bb62b5f9b..000000000 --- a/cmd/setup/11.sql +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN; --- create table with empty created_at -ALTER TABLE eventstore.events ADD COLUMN created_at TIMESTAMPTZ DEFAULT NULL; -COMMIT; - -BEGIN; --- backfill created_at -UPDATE eventstore.events SET created_at = creation_date WHERE created_at IS NULL; -COMMIT; - -BEGIN; --- set column rules -ALTER TABLE eventstore.events ALTER COLUMN created_at SET DEFAULT clock_timestamp(); -ALTER TABLE eventstore.events ALTER COLUMN created_at SET NOT NULL; -COMMIT; \ No newline at end of file diff --git a/cmd/setup/11/11_add_column.sql b/cmd/setup/11/11_add_column.sql new file mode 100644 index 000000000..0ba49b3d1 --- /dev/null +++ b/cmd/setup/11/11_add_column.sql @@ -0,0 +1,6 @@ +BEGIN; +-- create table with empty created_at +ALTER TABLE eventstore.events ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NULL; +-- set column rules +ALTER TABLE eventstore.events ALTER COLUMN created_at SET DEFAULT clock_timestamp(); +COMMIT; \ No newline at end of file diff --git a/cmd/setup/11/11_fetch_events.sql b/cmd/setup/11/11_fetch_events.sql new file mode 100644 index 000000000..7c7512526 --- /dev/null +++ b/cmd/setup/11/11_fetch_events.sql @@ -0,0 +1 @@ +SELECT id, creation_date FROM eventstore.events WHERE created_at IS NULL ORDER BY event_sequence DESC, instance_id LIMIT 20 FOR UPDATE \ No newline at end of file diff --git a/cmd/setup/11/11_fill_column.sql b/cmd/setup/11/11_fill_column.sql new file mode 100644 index 000000000..0a16913db --- /dev/null +++ b/cmd/setup/11/11_fill_column.sql @@ -0,0 +1 @@ +UPDATE eventstore.events SET created_at = $1 WHERE id = $2 \ No newline at end of file diff --git a/cmd/setup/11/11_set_column.sql b/cmd/setup/11/11_set_column.sql new file mode 100644 index 000000000..108bd182b --- /dev/null +++ b/cmd/setup/11/11_set_column.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE eventstore.events ALTER COLUMN created_at SET NOT NULL; +COMMIT; \ No newline at end of file From cea05953280ca42a3badb6a64693bbdbb817b856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Mon, 5 Jun 2023 18:43:51 +0200 Subject: [PATCH 11/76] Revert "feat: implement register Passkey user API v2 (#5873)" This reverts commit a301c40f9fc9a9b11ce8465aa0f8fe0dcbe5d09b. --- cmd/defaults.yaml | 4 - go.mod | 12 +- go.sum | 363 ++++++- internal/api/authz/user.go | 16 - internal/api/grpc/fields.go | 36 - .../settings/v2/settings_converter_test.go | 43 +- internal/api/grpc/user/v2/passkey.go | 104 -- .../grpc/user/v2/passkey_integration_test.go | 309 ------ internal/api/grpc/user/v2/passkey_test.go | 210 ---- internal/command/command.go | 18 +- internal/command/crypto.go | 50 +- internal/command/crypto_test.go | 242 ----- internal/command/email.go | 4 +- internal/command/user_human_test.go | 23 +- internal/command/user_human_webauthn.go | 2 +- internal/command/user_human_webauthn_model.go | 3 - internal/command/user_v2_email_test.go | 2 +- internal/command/user_v2_passkey.go | 156 --- internal/command/user_v2_passkey_test.go | 915 ------------------ internal/domain/human_email.go | 19 +- internal/domain/human_email_test.go | 38 +- internal/domain/url_template.go | 19 - internal/domain/url_template_test.go | 56 -- internal/domain/user_v2_passkey.go | 29 - internal/domain/user_v2_passkey_test.go | 54 -- .../notification/handlers/usernotifier.go | 5 +- .../types/email_verification_code_test.go | 35 +- .../types/passwordless_registration_link.go | 16 +- .../passwordless_registration_link_test.go | 88 -- internal/notification/types/types_test.go | 23 - .../repository/user/human_mfa_passwordless.go | 18 +- internal/static/i18n/de.yaml | 3 +- internal/static/i18n/en.yaml | 3 +- internal/static/i18n/es.yaml | 3 +- internal/static/i18n/fr.yaml | 3 +- internal/static/i18n/it.yaml | 3 +- internal/static/i18n/ja.yaml | 3 +- internal/static/i18n/pl.yaml | 3 +- internal/static/i18n/zh.yaml | 3 +- internal/webauthn/client.go | 36 - internal/webauthn/converter.go | 5 +- internal/webauthn/webauthn.go | 12 +- internal/webauthn/webauthn_test.go | 58 -- proto/zitadel/user/v2alpha/auth.proto | 2 +- 44 files changed, 519 insertions(+), 2530 deletions(-) delete mode 100644 internal/api/authz/user.go delete mode 100644 internal/api/grpc/fields.go delete mode 100644 internal/api/grpc/user/v2/passkey.go delete mode 100644 internal/api/grpc/user/v2/passkey_integration_test.go delete mode 100644 internal/api/grpc/user/v2/passkey_test.go delete mode 100644 internal/command/crypto_test.go delete mode 100644 internal/command/user_v2_passkey.go delete mode 100644 internal/command/user_v2_passkey_test.go delete mode 100644 internal/domain/url_template.go delete mode 100644 internal/domain/url_template_test.go delete mode 100644 internal/domain/user_v2_passkey.go delete mode 100644 internal/domain/user_v2_passkey_test.go delete mode 100644 internal/notification/types/passwordless_registration_link_test.go delete mode 100644 internal/notification/types/types_test.go delete mode 100644 internal/webauthn/client.go delete mode 100644 internal/webauthn/webauthn_test.go diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 4cc990b34..0867a1d5d 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -735,7 +735,6 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" - - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" @@ -812,7 +811,6 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" - - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" @@ -849,7 +847,6 @@ InternalAuthZ: - "user.grant.write" - "user.grant.delete" - "user.membership.read" - - "user.passkey.write" - "project.read" - "project.member.read" - "project.role.read" @@ -885,7 +882,6 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" - - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" diff --git a/go.mod b/go.mod index eeca72a61..102eda91a 100644 --- a/go.mod +++ b/go.mod @@ -15,13 +15,12 @@ require ( github.com/benbjohnson/clock v1.3.0 github.com/boombuler/barcode v1.0.1 github.com/cockroachdb/cockroach-go/v2 v2.3.3 - github.com/descope/virtualwebauthn v1.0.2 github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 github.com/drone/envsubst v1.0.3 + github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec github.com/envoyproxy/protoc-gen-validate v0.10.1 github.com/go-ldap/ldap/v3 v3.4.4 - github.com/go-webauthn/webauthn v0.8.2 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 @@ -71,7 +70,7 @@ require ( go.opentelemetry.io/otel/sdk v1.14.0 go.opentelemetry.io/otel/sdk/metric v0.37.0 go.opentelemetry.io/otel/trace v1.14.0 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.7.0 golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.8.0 golang.org/x/sync v0.1.0 @@ -87,16 +86,12 @@ require ( require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 // indirect + github.com/cloudflare/cfssl v1.6.3 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-sql-driver/mysql v1.6.0 // indirect - github.com/go-webauthn/revoke v0.1.9 // indirect - github.com/google/go-tpm v0.3.3 // indirect github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect - github.com/gorilla/websocket v1.4.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/smartystreets/assertions v1.0.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect go.uber.org/multierr v1.11.0 // indirect ) @@ -141,6 +136,7 @@ require ( github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/certificate-transparency-go v1.1.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect diff --git a/go.sum b/go.sum index 716d80a78..be8a76d22 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,10 @@ +bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= +bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -17,6 +21,9 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -31,6 +38,7 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc= cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= @@ -40,6 +48,7 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= @@ -50,7 +59,21 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= +contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= +contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= +contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= +contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= +contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= +github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= @@ -61,18 +84,24 @@ github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 h1:aRQEQ57Mw12h0tG8oo2UGC5d8fpUFCvD1lcS9fdGh6I= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0/go.mod h1:WzE/bKzbWw91rEv1w53y4taJheFSkUzp2Mu8uItorHg= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.37.0 h1:+3ioRuJPNQk6WkbdClWVo8FyfpXu/ZXePQ3syDwrN4o= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 h1:k5x4SiDgKS8wVQO/ww1fnoh5gwYEg6Wsi+1z5kB9uDM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0/go.mod h1:oEccMakRmMNrayCPR+5OmZE/aeXmTPzUtmomEXIPBdI= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= @@ -89,6 +118,8 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -100,17 +131,32 @@ github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc 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/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= +github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= +github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -121,15 +167,24 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= +github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -142,9 +197,14 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY= +github.com/cloudflare/cfssl v1.6.3 h1:hDhRaGQN55nh0510/7A5QBN3xLoDz/M7nQX80icXvzs= +github.com/cloudflare/cfssl v1.6.3/go.mod h1:Kq0iHKY8sm2klDeQ2Ci/FI+6QdBGuyPWodgTJFLrXIw= +github.com/cloudflare/redoctober v0.0.0-20201013214028-99c99a8e7544/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210322005330-6414d713912e/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -155,14 +215,21 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4= github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -171,17 +238,19 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/descope/virtualwebauthn v1.0.2 h1:cAvfS9wHh6On9HAE4Gjn3fJkf8MPQW2LzN8BPKEPs0M= -github.com/descope/virtualwebauthn v1.0.2/go.mod h1:iJvinjD1iZYqQ09J5lF0+795OdDbzTWcYQjPD/BF54M= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= @@ -226,44 +295,62 @@ github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4= github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo= github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= +github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec h1:darQ1FPPrwlzwmuN3fRMVCrsaCpuDqkKHADYzcMa73M= +github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec/go.mod h1:V3q8IgNpNqFio+56G0vy/QZIi7iho65UFrDwdF5OtZA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fullstorydev/grpcurl v1.8.0/go.mod h1:Mn2jWbdMrQGJQ8UD62uNyMumT2acsZUCkZIqFxsQf1o= +github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDePli4ecpK1N7gw= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -274,6 +361,7 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -295,17 +383,15 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= -github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= -github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= -github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= @@ -315,6 +401,8 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -324,6 +412,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -335,6 +425,7 @@ github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgR github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc h1:WkAZHSmcnJhZyutVoVXe7lDSQBbISxITcm57tYf22PE= github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -352,6 +443,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -369,6 +461,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -377,6 +470,12 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= +github.com/google/certificate-transparency-go v1.1.2-0.20210422104406-9f33727a7a18/go.mod h1:6CKh9dscIRoqc2kC6YUFICHZMT9NrClyPrRVFrdw1QQ= +github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92/go.mod h1:kXWPsHVPSKVuxPPG69BRtumCbAW537FydV/GH89oBhM= +github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= +github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -391,15 +490,16 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= -github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= -github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= -github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= +github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE= +github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= @@ -413,16 +513,27 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk= github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/trillian v1.3.14-0.20210409160123-c5ea3abd4a41/go.mod h1:1dPv0CUjNQVFEDuAUFhZql16pw/VlPgaX8qj+g5pVzQ= +github.com/google/trillian v1.3.14-0.20210428093031-b4ddea2e86b1/go.mod h1:FdIJX+NoDk/dIN2ZxTyz5nAJWgf+NSSSriPAMThChTY= +github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= @@ -430,6 +541,9 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= +github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE= github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= @@ -451,11 +565,15 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= @@ -468,13 +586,17 @@ github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -491,12 +613,18 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= @@ -553,33 +681,44 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52/go.mod h1:RDZ+4PR3mDOtTpVbI0qBE+rdhmtIrtbssiNn38/1OWA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= +github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= +github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k3a/html2text v1.1.0 h1:ks4hKSTdiTRsLr0DM771mI5TvsoG6zH7m1Ulv7eJRHw= @@ -588,11 +727,15 @@ github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 h1:K8qael4Lems github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw= github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 h1:2b9anKtyO/UTUQb+TAdPAW+w0p9xCIvng4fdZJ2xsYk= github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2 h1:k+lYMvS9cAl7e4Ea78qodfa6QZfXNa4QlFS/0GYpanI= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2/go.mod h1:PDdDH7RSKjjy9iFyoMzfeChOSmXpXuMEUqmAJSihxx4= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= +github.com/kisom/goutils v1.4.3/go.mod h1:Lp5qrquG7yhYnWzZCI/68Pa/GpFynw//od6EkGnWpac= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= @@ -615,16 +758,20 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2KKkQWxfJUcU= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -632,27 +779,44 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= @@ -660,6 +824,7 @@ github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9 github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -671,6 +836,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -680,6 +847,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 h1:p4A2Jx7Lm3NV98VRMKlyWd3nqf8obft8NfXlAUmqd3I= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= @@ -692,6 +861,8 @@ github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= +github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -704,13 +875,19 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= +github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -720,9 +897,15 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= @@ -735,7 +918,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -746,7 +931,9 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -762,8 +949,11 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -772,14 +962,21 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= +github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -800,13 +997,18 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -817,14 +1019,20 @@ github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sony/sonyflake v1.1.0 h1:wnrEcL3aOkWmPlhScLEGAXKkLAIslnBteNUq4Bw6MM4= github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -833,6 +1041,8 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -844,8 +1054,10 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -854,6 +1066,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -866,12 +1079,20 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203/go.mod h1:0Xw5cYMOYpgaWs+OOSx41ugycl2qvKTi9tlMMcZhFyY= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w= github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4= @@ -882,11 +1103,20 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA= +github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= +github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -904,9 +1134,26 @@ github.com/zitadel/oidc/v2 v2.6.1 h1:NIW4/KPi9Q2Pae6MKEhrSl+DSC5W4574GlboHildroc github.com/zitadel/oidc/v2 v2.6.1/go.mod h1:vsFNrYCj2x0it0pYmIRVZ12HJ1VGaMVyGl7HLqw5p+Y= github.com/zitadel/saml v0.0.11 h1:kObucnBrcu1PHCO7RGT0iVeuJL/5I50gUgr40S41nMs= github.com/zitadel/saml v0.0.11/go.mod h1:YGWAvPZRv4DbEZ78Ht/2P0AWzGn+6WGhFf90PMXl0Po= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= +github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= +github.com/zmap/zcrypto v0.0.0-20210123152837-9cf5beac6d91/go.mod h1:R/deQh6+tSWlgI9tb4jNmXxn8nSCabl5ZQsBX9//I/E= +github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk= +github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= +go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= +go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= +go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0/go.mod h1:YPwSaBciV5G6Gpt435AasAG3ROetZsKNUzibRa/++oo= +go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY= +go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w= +go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= +go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0/go.mod h1:HnrHxjyCuZ8YDt8PYVyQQ5d1ZQfzJVEtQWllr5Vp/30= +go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i28snx8To= +go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -915,6 +1162,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= @@ -955,26 +1203,36 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= +golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -982,9 +1240,10 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1010,6 +1269,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1029,6 +1289,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1042,10 +1303,14 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1065,9 +1330,12 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1077,7 +1345,9 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1085,6 +1355,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= @@ -1092,6 +1366,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1112,6 +1387,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1120,12 +1396,14 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1149,17 +1427,25 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1197,15 +1483,19 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1213,15 +1503,19 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191118222007-07fc4c7f2b98/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1239,15 +1533,19 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1269,9 +1567,12 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -1287,23 +1588,32 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= google.golang.org/api v0.115.0 h1:6FFkVvStt4YqXSx3azKyzj7fXerGnVlLJ/eud01nBDE= google.golang.org/api v0.115.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1337,10 +1647,19 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210331142528-b7513248f0ba/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU= google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1365,10 +1684,13 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1379,6 +1701,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -1391,15 +1714,20 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= 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= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1426,13 +1754,16 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/internal/api/authz/user.go b/internal/api/authz/user.go deleted file mode 100644 index 13dc4076f..000000000 --- a/internal/api/authz/user.go +++ /dev/null @@ -1,16 +0,0 @@ -package authz - -import ( - "context" - - "github.com/zitadel/zitadel/internal/errors" -) - -// UserIDInCTX checks if the userID -// equals the authenticated user in the context. -func UserIDInCTX(ctx context.Context, userID string) error { - if GetCtxData(ctx).UserID != userID { - return errors.ThrowUnauthenticated(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong") - } - return nil -} diff --git a/internal/api/grpc/fields.go b/internal/api/grpc/fields.go deleted file mode 100644 index f0c9d30a6..000000000 --- a/internal/api/grpc/fields.go +++ /dev/null @@ -1,36 +0,0 @@ -package grpc - -import ( - "testing" - - "google.golang.org/protobuf/reflect/protoreflect" -) - -// AllFieldsSet recusively checks if all values in a message -// have a non-zero value. -func AllFieldsSet(t testing.TB, msg protoreflect.Message, ignoreTypes ...protoreflect.FullName) { - ignore := make(map[protoreflect.FullName]bool, len(ignoreTypes)) - for _, name := range ignoreTypes { - ignore[name] = true - } - - md := msg.Descriptor() - name := md.FullName() - if ignore[name] { - return - } - - fields := md.Fields() - - for i := 0; i < fields.Len(); i++ { - fd := fields.Get(i) - if !msg.Has(fd) { - t.Errorf("not all fields set in %q, missing %q", name, fd.Name()) - continue - } - - if fd.Kind() == protoreflect.MessageKind { - AllFieldsSet(t, msg.Get(fd).Message(), ignoreTypes...) - } - } -} diff --git a/internal/api/grpc/settings/v2/settings_converter_test.go b/internal/api/grpc/settings/v2/settings_converter_test.go index 11ebc0f71..818bf64ff 100644 --- a/internal/api/grpc/settings/v2/settings_converter_test.go +++ b/internal/api/grpc/settings/v2/settings_converter_test.go @@ -11,13 +11,38 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/durationpb" - "github.com/zitadel/zitadel/internal/api/grpc" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha" ) -var ignoreTypes = []protoreflect.FullName{"google.protobuf.Duration"} +var ignoreMessageTypes = map[protoreflect.FullName]bool{ + "google.protobuf.Duration": true, +} + +// allFieldsSet recusively checks if all values in a message +// have a non-zero value. +func allFieldsSet(t testing.TB, msg protoreflect.Message) { + md := msg.Descriptor() + name := md.FullName() + if ignoreMessageTypes[name] { + return + } + + fields := md.Fields() + + for i := 0; i < fields.Len(); i++ { + fd := fields.Get(i) + if !msg.Has(fd) { + t.Errorf("not all fields set in %q, missing %q", name, fd.Name()) + continue + } + + if fd.Kind() == protoreflect.MessageKind { + allFieldsSet(t, msg.Get(fd).Message()) + } + } +} func Test_loginSettingsToPb(t *testing.T) { arg := &query.LoginPolicy{ @@ -75,7 +100,7 @@ func Test_loginSettingsToPb(t *testing.T) { } got := loginSettingsToPb(arg) - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("loginSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -216,7 +241,7 @@ func Test_passwordSettingsToPb(t *testing.T) { } got := passwordSettingsToPb(arg) - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("passwordSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -270,7 +295,7 @@ func Test_brandingSettingsToPb(t *testing.T) { } got := brandingSettingsToPb(arg, "http://example.com") - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("brandingSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -290,7 +315,7 @@ func Test_domainSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := domainSettingsToPb(arg) - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("domainSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -312,7 +337,7 @@ func Test_legalSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := legalAndSupportSettingsToPb(arg) - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("legalSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -328,7 +353,7 @@ func Test_lockoutSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := lockoutSettingsToPb(arg) - grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, got.ProtoReflect()) if !proto.Equal(got, want) { t.Errorf("lockoutSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -362,7 +387,7 @@ func Test_identityProvidersToPb(t *testing.T) { got := identityProvidersToPb(arg) require.Len(t, got, len(got)) for i, v := range got { - grpc.AllFieldsSet(t, v.ProtoReflect(), ignoreTypes...) + allFieldsSet(t, v.ProtoReflect()) if !proto.Equal(v, want[i]) { t.Errorf("identityProvidersToPb() =\n%v\nwant\n%v", got, want) } diff --git a/internal/api/grpc/user/v2/passkey.go b/internal/api/grpc/user/v2/passkey.go deleted file mode 100644 index eb1b276f8..000000000 --- a/internal/api/grpc/user/v2/passkey.go +++ /dev/null @@ -1,104 +0,0 @@ -package user - -import ( - "context" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/api/grpc/object/v2" - "github.com/zitadel/zitadel/internal/domain" - caos_errs "github.com/zitadel/zitadel/internal/errors" - user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" -) - -func (s *Server) RegisterPasskey(ctx context.Context, req *user.RegisterPasskeyRequest) (resp *user.RegisterPasskeyResponse, err error) { - var ( - resourceOwner = authz.GetCtxData(ctx).ResourceOwner - authenticator = passkeyAuthenticatorToDomain(req.GetAuthenticator()) - ) - if code := req.GetCode(); code != nil { - return passkeyRegistrationDetailsToPb( - s.command.RegisterUserPasskeyWithCode(ctx, req.GetUserId(), resourceOwner, authenticator, code.Id, code.Code, s.userCodeAlg), - ) - } - return passkeyRegistrationDetailsToPb( - s.command.RegisterUserPasskey(ctx, req.GetUserId(), resourceOwner, authenticator), - ) -} - -func passkeyAuthenticatorToDomain(pa user.PasskeyAuthenticator) domain.AuthenticatorAttachment { - switch pa { - case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_UNSPECIFIED: - return domain.AuthenticatorAttachmentUnspecified - case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM: - return domain.AuthenticatorAttachmentPlattform - case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM: - return domain.AuthenticatorAttachmentCrossPlattform - default: - return domain.AuthenticatorAttachmentUnspecified - } -} - -func passkeyRegistrationDetailsToPb(details *domain.PasskeyRegistrationDetails, err error) (*user.RegisterPasskeyResponse, error) { - if err != nil { - return nil, err - } - return &user.RegisterPasskeyResponse{ - Details: object.DomainToDetailsPb(details.ObjectDetails), - PasskeyId: details.PasskeyID, - PublicKeyCredentialCreationOptions: details.PublicKeyCredentialCreationOptions, - }, nil -} - -func (s *Server) VerifyPasskeyRegistration(ctx context.Context, req *user.VerifyPasskeyRegistrationRequest) (*user.VerifyPasskeyRegistrationResponse, error) { - resourceOwner := authz.GetCtxData(ctx).ResourceOwner - objectDetails, err := s.command.HumanHumanPasswordlessSetup(ctx, req.GetUserId(), resourceOwner, req.GetPasskeyName(), "", req.GetPublicKeyCredential()) - if err != nil { - return nil, err - } - return &user.VerifyPasskeyRegistrationResponse{ - Details: object.DomainToDetailsPb(objectDetails), - }, nil -} - -func (s *Server) CreatePasskeyRegistrationLink(ctx context.Context, req *user.CreatePasskeyRegistrationLinkRequest) (resp *user.CreatePasskeyRegistrationLinkResponse, err error) { - resourceOwner := authz.GetCtxData(ctx).ResourceOwner - - switch medium := req.Medium.(type) { - case nil: - return passkeyDetailsToPb( - s.command.AddUserPasskeyCode(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), - ) - case *user.CreatePasskeyRegistrationLinkRequest_SendLink: - return passkeyDetailsToPb( - s.command.AddUserPasskeyCodeURLTemplate(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg, medium.SendLink.GetUrlTemplate()), - ) - case *user.CreatePasskeyRegistrationLinkRequest_ReturnCode: - return passkeyCodeDetailsToPb( - s.command.AddUserPasskeyCodeReturn(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), - ) - default: - return nil, caos_errs.ThrowUnimplementedf(nil, "USERv2-gaD8y", "verification oneOf %T in method CreatePasskeyRegistrationLink not implemented", medium) - } -} - -func passkeyDetailsToPb(details *domain.ObjectDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { - if err != nil { - return nil, err - } - return &user.CreatePasskeyRegistrationLinkResponse{ - Details: object.DomainToDetailsPb(details), - }, nil -} - -func passkeyCodeDetailsToPb(details *domain.PasskeyCodeDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { - if err != nil { - return nil, err - } - return &user.CreatePasskeyRegistrationLinkResponse{ - Details: object.DomainToDetailsPb(details.ObjectDetails), - Code: &user.PasskeyRegistrationCode{ - Id: details.CodeID, - Code: details.Code, - }, - }, nil -} diff --git a/internal/api/grpc/user/v2/passkey_integration_test.go b/internal/api/grpc/user/v2/passkey_integration_test.go deleted file mode 100644 index 623b2544f..000000000 --- a/internal/api/grpc/user/v2/passkey_integration_test.go +++ /dev/null @@ -1,309 +0,0 @@ -//go:build integration - -package user_test - -import ( - "context" - "testing" - - "github.com/muhlemmer/gu" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/internal/webauthn" - object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha" - user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" -) - -func TestServer_RegisterPasskey(t *testing.T) { - userID := createHumanUser(t).GetUserId() - reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{ - UserId: userID, - Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, - }) - require.NoError(t, err) - client := webauthn.NewClient(Tester.Config.WebAuthNName, Tester.Config.ExternalDomain, "https://"+Tester.Host()) - - type args struct { - ctx context.Context - req *user.RegisterPasskeyRequest - } - tests := []struct { - name string - args args - want *user.RegisterPasskeyResponse - wantErr bool - }{ - { - name: "missing user id", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{}, - }, - wantErr: true, - }, - { - name: "register code", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{ - UserId: userID, - Code: reg.GetCode(), - Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, - }, - }, - want: &user.RegisterPasskeyResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - }, - { - name: "reuse code (not allowed)", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{ - UserId: userID, - Code: reg.GetCode(), - Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, - }, - }, - wantErr: true, - }, - { - name: "wrong code", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{ - UserId: userID, - Code: &user.PasskeyRegistrationCode{ - Id: reg.GetCode().GetId(), - Code: "foobar", - }, - Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM, - }, - }, - wantErr: true, - }, - { - name: "user mismatch", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{ - UserId: userID, - }, - }, - wantErr: true, - }, - /* TODO after we are able to obtain a Bearer token for a human user - { - name: "human user", - args: args{ - ctx: CTX, - req: &user.RegisterPasskeyRequest{ - UserId: humanUserID, - }, - }, - want: &user.RegisterPasskeyResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - }, - */ - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Client.RegisterPasskey(tt.args.ctx, tt.args.req) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.NotNil(t, got) - integration.AssertDetails(t, tt.want, got) - if tt.want != nil { - assert.NotEmpty(t, got.GetPasskeyId()) - assert.NotEmpty(t, got.GetPublicKeyCredentialCreationOptions()) - _, err := client.CreateAttestationResponse(got.GetPublicKeyCredentialCreationOptions()) - require.NoError(t, err) - } - }) - } -} - -func TestServer_VerifyPasskeyRegistration(t *testing.T) { - userID := createHumanUser(t).GetUserId() - reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{ - UserId: userID, - Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, - }) - require.NoError(t, err) - pkr, err := Client.RegisterPasskey(CTX, &user.RegisterPasskeyRequest{ - UserId: userID, - Code: reg.GetCode(), - }) - require.NoError(t, err) - require.NotEmpty(t, pkr.GetPasskeyId()) - require.NotEmpty(t, pkr.GetPublicKeyCredentialCreationOptions()) - - client := webauthn.NewClient(Tester.Config.WebAuthNName, Tester.Config.ExternalDomain, "https://"+Tester.Host()) - attestationResponse, err := client.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions()) - require.NoError(t, err) - - type args struct { - ctx context.Context - req *user.VerifyPasskeyRegistrationRequest - } - tests := []struct { - name string - args args - want *user.VerifyPasskeyRegistrationResponse - wantErr bool - }{ - { - name: "missing user id", - args: args{ - ctx: CTX, - req: &user.VerifyPasskeyRegistrationRequest{ - PasskeyId: pkr.GetPasskeyId(), - PublicKeyCredential: []byte(attestationResponse), - PasskeyName: "nice name", - }, - }, - wantErr: true, - }, - { - name: "success", - args: args{ - ctx: CTX, - req: &user.VerifyPasskeyRegistrationRequest{ - UserId: userID, - PasskeyId: pkr.GetPasskeyId(), - PublicKeyCredential: attestationResponse, - PasskeyName: "nice name", - }, - }, - want: &user.VerifyPasskeyRegistrationResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - }, - { - name: "wrong credential", - args: args{ - ctx: CTX, - req: &user.VerifyPasskeyRegistrationRequest{ - UserId: userID, - PasskeyId: pkr.GetPasskeyId(), - PublicKeyCredential: []byte("attestationResponseattestationResponseattestationResponse"), - PasskeyName: "nice name", - }, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Client.VerifyPasskeyRegistration(tt.args.ctx, tt.args.req) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.NotNil(t, got) - integration.AssertDetails(t, tt.want, got) - }) - } -} - -func TestServer_CreatePasskeyRegistrationLink(t *testing.T) { - userID := createHumanUser(t).GetUserId() - - type args struct { - ctx context.Context - req *user.CreatePasskeyRegistrationLinkRequest - } - tests := []struct { - name string - args args - want *user.CreatePasskeyRegistrationLinkResponse - wantCode bool - wantErr bool - }{ - { - name: "missing user id", - args: args{ - ctx: CTX, - req: &user.CreatePasskeyRegistrationLinkRequest{}, - }, - wantErr: true, - }, - { - name: "send default mail", - args: args{ - ctx: CTX, - req: &user.CreatePasskeyRegistrationLinkRequest{ - UserId: userID, - }, - }, - want: &user.CreatePasskeyRegistrationLinkResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - }, - { - name: "send custom url", - args: args{ - ctx: CTX, - req: &user.CreatePasskeyRegistrationLinkRequest{ - UserId: userID, - Medium: &user.CreatePasskeyRegistrationLinkRequest_SendLink{ - SendLink: &user.SendPasskeyRegistrationLink{ - UrlTemplate: gu.Ptr("https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}"), - }, - }, - }, - }, - want: &user.CreatePasskeyRegistrationLinkResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - }, - { - name: "return code", - args: args{ - ctx: CTX, - req: &user.CreatePasskeyRegistrationLinkRequest{ - UserId: userID, - Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, - }, - }, - want: &user.CreatePasskeyRegistrationLinkResponse{ - Details: &object.Details{ - ResourceOwner: Tester.Organisation.ID, - }, - }, - wantCode: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Client.CreatePasskeyRegistrationLink(tt.args.ctx, tt.args.req) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.NotNil(t, got) - integration.AssertDetails(t, tt.want, got) - if tt.wantCode { - assert.NotEmpty(t, got.GetCode().GetId()) - assert.NotEmpty(t, got.GetCode().GetId()) - } - }) - } -} diff --git a/internal/api/grpc/user/v2/passkey_test.go b/internal/api/grpc/user/v2/passkey_test.go deleted file mode 100644 index d1934c3ca..000000000 --- a/internal/api/grpc/user/v2/passkey_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package user - -import ( - "io" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/zitadel/zitadel/internal/api/grpc" - "github.com/zitadel/zitadel/internal/domain" - object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha" - user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" -) - -func Test_passkeyAuthenticatorToDomain(t *testing.T) { - tests := []struct { - pa user.PasskeyAuthenticator - want domain.AuthenticatorAttachment - }{ - { - pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_UNSPECIFIED, - want: domain.AuthenticatorAttachmentUnspecified, - }, - { - pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, - want: domain.AuthenticatorAttachmentPlattform, - }, - { - pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM, - want: domain.AuthenticatorAttachmentCrossPlattform, - }, - { - pa: 999, - want: domain.AuthenticatorAttachmentUnspecified, - }, - } - for _, tt := range tests { - t.Run(tt.pa.String(), func(t *testing.T) { - got := passkeyAuthenticatorToDomain(tt.pa) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_passkeyRegistrationDetailsToPb(t *testing.T) { - type args struct { - details *domain.PasskeyRegistrationDetails - err error - } - tests := []struct { - name string - args args - want *user.RegisterPasskeyResponse - }{ - { - name: "an error", - args: args{ - details: nil, - err: io.ErrClosedPipe, - }, - }, - { - name: "ok", - args: args{ - details: &domain.PasskeyRegistrationDetails{ - ObjectDetails: &domain.ObjectDetails{ - Sequence: 22, - EventDate: time.Unix(3000, 22), - ResourceOwner: "me", - }, - PasskeyID: "123", - PublicKeyCredentialCreationOptions: []byte{1, 2, 3}, - }, - err: nil, - }, - want: &user.RegisterPasskeyResponse{ - Details: &object.Details{ - Sequence: 22, - ChangeDate: ×tamppb.Timestamp{ - Seconds: 3000, - Nanos: 22, - }, - ResourceOwner: "me", - }, - PasskeyId: "123", - PublicKeyCredentialCreationOptions: []byte{1, 2, 3}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := passkeyRegistrationDetailsToPb(tt.args.details, tt.args.err) - require.ErrorIs(t, err, tt.args.err) - assert.Equal(t, tt.want, got) - if tt.want != nil { - grpc.AllFieldsSet(t, got.ProtoReflect()) - } - }) - } -} - -func Test_passkeyDetailsToPb(t *testing.T) { - type args struct { - details *domain.ObjectDetails - err error - } - tests := []struct { - name string - args args - want *user.CreatePasskeyRegistrationLinkResponse - }{ - { - name: "an error", - args: args{ - details: nil, - err: io.ErrClosedPipe, - }, - }, - { - name: "ok", - args: args{ - details: &domain.ObjectDetails{ - Sequence: 22, - EventDate: time.Unix(3000, 22), - ResourceOwner: "me", - }, - err: nil, - }, - want: &user.CreatePasskeyRegistrationLinkResponse{ - Details: &object.Details{ - Sequence: 22, - ChangeDate: ×tamppb.Timestamp{ - Seconds: 3000, - Nanos: 22, - }, - ResourceOwner: "me", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := passkeyDetailsToPb(tt.args.details, tt.args.err) - require.ErrorIs(t, err, tt.args.err) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_passkeyCodeDetailsToPb(t *testing.T) { - type args struct { - details *domain.PasskeyCodeDetails - err error - } - tests := []struct { - name string - args args - want *user.CreatePasskeyRegistrationLinkResponse - }{ - { - name: "an error", - args: args{ - details: nil, - err: io.ErrClosedPipe, - }, - }, - { - name: "ok", - args: args{ - details: &domain.PasskeyCodeDetails{ - ObjectDetails: &domain.ObjectDetails{ - Sequence: 22, - EventDate: time.Unix(3000, 22), - ResourceOwner: "me", - }, - CodeID: "123", - Code: "456", - }, - err: nil, - }, - want: &user.CreatePasskeyRegistrationLinkResponse{ - Details: &object.Details{ - Sequence: 22, - ChangeDate: ×tamppb.Timestamp{ - Seconds: 3000, - Nanos: 22, - }, - ResourceOwner: "me", - }, - Code: &user.PasskeyRegistrationCode{ - Id: "123", - Code: "456", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := passkeyCodeDetailsToPb(tt.args.details, tt.args.err) - require.ErrorIs(t, err, tt.args.err) - assert.Equal(t, tt.want, got) - if tt.want != nil { - grpc.AllFieldsSet(t, got.ProtoReflect()) - } - }) - } -} diff --git a/internal/command/command.go b/internal/command/command.go index 3c804576b..28e869af2 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -32,7 +32,7 @@ type Commands struct { httpClient *http.Client checkPermission domain.PermissionCheck - newCode cryptoCodeFunc + newEmailCode func(ctx context.Context, filter preparation.FilterToQueryReducer, codeAlg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) eventstore *eventstore.Eventstore static static.Storage @@ -109,7 +109,7 @@ func StartCommands( webauthnConfig: webAuthN, httpClient: httpClient, checkPermission: permissionCheck, - newCode: newCryptoCodeWithExpiry, + newEmailCode: newEmailCode, sessionTokenCreator: sessionTokenCreator(idGenerator, sessionAlg), sessionTokenVerifier: sessionTokenVerifier, } @@ -141,21 +141,11 @@ func StartCommands( return repo, nil } -type AppendReducer interface { +func AppendAndReduce(object interface { AppendEvents(...eventstore.Event) // TODO: Why is it allowed to return an error here? Reduce() error -} - -func (c *Commands) pushAppendAndReduce(ctx context.Context, object AppendReducer, cmds ...eventstore.Command) error { - events, err := c.eventstore.Push(ctx, cmds...) - if err != nil { - return err - } - return AppendAndReduce(object, events...) -} - -func AppendAndReduce(object AppendReducer, events ...eventstore.Event) error { +}, events ...eventstore.Event) error { object.AppendEvents(events...) return object.Reduce() } diff --git a/internal/command/crypto.go b/internal/command/crypto.go index e94f66463..404b2221f 100644 --- a/internal/command/crypto.go +++ b/internal/command/crypto.go @@ -10,8 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/errors" ) -type cryptoCodeFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) - type CryptoCodeWithExpiry struct { Crypted *crypto.CryptoValue Plain string @@ -19,50 +17,42 @@ type CryptoCodeWithExpiry struct { } func newCryptoCodeWithExpiry(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) { - gen, config, err := secretGenerator(ctx, filter, typ, alg) + config, err := secretGeneratorConfig(ctx, filter, typ) if err != nil { return nil, err } - crypted, plain, err := crypto.NewCode(gen) - if err != nil { - return nil, err - } - return &CryptoCodeWithExpiry{ - Crypted: crypted, - Plain: plain, - Expiry: config.Expiry, - }, nil -} -func verifyCryptoCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, creation time.Time, expiry time.Duration, crypted *crypto.CryptoValue, plain string) error { - gen, _, err := secretGenerator(ctx, filter, typ, alg) - if err != nil { - return err + code := new(CryptoCodeWithExpiry) + switch a := alg.(type) { + case crypto.HashAlgorithm: + code.Crypted, code.Plain, err = crypto.NewCode(crypto.NewHashGenerator(*config, a)) + case crypto.EncryptionAlgorithm: + code.Crypted, code.Plain, err = crypto.NewCode(crypto.NewEncryptionGenerator(*config, a)) + default: + return nil, errors.ThrowInternal(nil, "COMMA-RreV6", "Errors.Internal") } - return crypto.VerifyCode(creation, expiry, crypted, plain, gen) + if err != nil { + return nil, err + } + + code.Expiry = config.Expiry + return code, nil } func newCryptoCodeWithPlain(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (value *crypto.CryptoValue, plain string, err error) { - gen, _, err := secretGenerator(ctx, filter, typ, alg) + config, err := secretGeneratorConfig(ctx, filter, typ) if err != nil { return nil, "", err } - return crypto.NewCode(gen) -} -func secretGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (crypto.Generator, *crypto.GeneratorConfig, error) { - config, err := secretGeneratorConfig(ctx, filter, typ) - if err != nil { - return nil, nil, err - } switch a := alg.(type) { case crypto.HashAlgorithm: - return crypto.NewHashGenerator(*config, a), config, nil + return crypto.NewCode(crypto.NewHashGenerator(*config, a)) case crypto.EncryptionAlgorithm: - return crypto.NewEncryptionGenerator(*config, a), config, nil - default: - return nil, nil, errors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", a) + return crypto.NewCode(crypto.NewEncryptionGenerator(*config, a)) } + + return nil, "", errors.ThrowInvalidArgument(nil, "V2-NGESt", "Errors.Internal") } func secretGeneratorConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType) (*crypto.GeneratorConfig, error) { diff --git a/internal/command/crypto_test.go b/internal/command/crypto_test.go deleted file mode 100644 index a846ae43a..000000000 --- a/internal/command/crypto_test.go +++ /dev/null @@ -1,242 +0,0 @@ -package command - -import ( - "context" - "io" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func mockCode(code string, exp time.Duration) cryptoCodeFunc { - return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) { - return &CryptoCodeWithExpiry{ - Crypted: &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte(code), - }, - Plain: code, - Expiry: exp, - }, nil - } -} - -var ( - testGeneratorConfig = crypto.GeneratorConfig{ - Length: 12, - Expiry: 60000000000, - IncludeLowerLetters: true, - IncludeUpperLetters: true, - IncludeDigits: true, - IncludeSymbols: true, - } -) - -func testSecretGeneratorAddedEvent(typ domain.SecretGeneratorType) *instance.SecretGeneratorAddedEvent { - return instance.NewSecretGeneratorAddedEvent(context.Background(), - &instance.NewAggregate("inst1").Aggregate, typ, - testGeneratorConfig.Length, - testGeneratorConfig.Expiry, - testGeneratorConfig.IncludeLowerLetters, - testGeneratorConfig.IncludeUpperLetters, - testGeneratorConfig.IncludeDigits, - testGeneratorConfig.IncludeSymbols, - ) -} - -func Test_newCryptoCode(t *testing.T) { - type args struct { - typ domain.SecretGeneratorType - alg crypto.Crypto - } - tests := []struct { - name string - eventstore *eventstore.Eventstore - args args - wantErr error - }{ - { - name: "filter config error", - eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - eventstore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := newCryptoCodeWithExpiry(context.Background(), tt.eventstore.Filter, tt.args.typ, tt.args.alg) - require.ErrorIs(t, err, tt.wantErr) - if tt.wantErr == nil { - require.NotNil(t, got) - assert.NotNil(t, got.Crypted) - assert.NotEmpty(t, got) - assert.Equal(t, testGeneratorConfig.Expiry, got.Expiry) - } - }) - } -} - -func Test_verifyCryptoCode(t *testing.T) { - es := eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )) - code, err := newCryptoCodeWithExpiry(context.Background(), es.Filter, domain.SecretGeneratorTypeVerifyEmailCode, crypto.CreateMockHashAlg(gomock.NewController(t))) - require.NoError(t, err) - - type args struct { - typ domain.SecretGeneratorType - alg crypto.Crypto - expiry time.Duration - crypted *crypto.CryptoValue - plain string - } - tests := []struct { - name string - eventsore *eventstore.Eventstore - args args - wantErr bool - }{ - { - name: "filter config error", - eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - expiry: code.Expiry, - crypted: code.Crypted, - plain: code.Plain, - }, - wantErr: true, - }, - { - name: "success", - eventsore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - expiry: code.Expiry, - crypted: code.Crypted, - plain: code.Plain, - }, - }, - { - name: "wrong plain", - eventsore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - expiry: code.Expiry, - crypted: code.Crypted, - plain: "wrong", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := verifyCryptoCode(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, time.Now(), tt.args.expiry, tt.args.crypted, tt.args.plain) - if tt.wantErr { - assert.Error(t, err) - return - } - require.NoError(t, err) - }) - } -} - -func Test_secretGenerator(t *testing.T) { - type args struct { - typ domain.SecretGeneratorType - alg crypto.Crypto - } - tests := []struct { - name string - eventsore *eventstore.Eventstore - args args - want crypto.Generator - wantConf *crypto.GeneratorConfig - wantErr error - }{ - { - name: "filter config error", - eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "hash generator", - eventsore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockHashAlg(gomock.NewController(t)), - }, - want: crypto.NewHashGenerator(testGeneratorConfig, crypto.CreateMockHashAlg(gomock.NewController(t))), - wantConf: &testGeneratorConfig, - }, - { - name: "encryption generator", - eventsore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - }, - want: crypto.NewEncryptionGenerator(testGeneratorConfig, crypto.CreateMockEncryptionAlg(gomock.NewController(t))), - wantConf: &testGeneratorConfig, - }, - { - name: "unsupported type", - eventsore: eventstoreExpect(t, expectFilter( - eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), - )), - args: args{ - typ: domain.SecretGeneratorTypeVerifyEmailCode, - alg: nil, - }, - wantErr: errors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", nil), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, gotConf, err := secretGenerator(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg) - require.ErrorIs(t, err, tt.wantErr) - assert.IsType(t, tt.want, got) - assert.Equal(t, tt.wantConf, gotConf) - }) - } -} diff --git a/internal/command/email.go b/internal/command/email.go index f54b3de8e..0bfbcd6af 100644 --- a/internal/command/email.go +++ b/internal/command/email.go @@ -23,6 +23,6 @@ func (e *Email) Validate() error { return e.Address.Validate() } -func (c *Commands) newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { - return c.newCode(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg) +func newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { + return newCryptoCodeWithExpiry(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg) } diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go index 5d6f131c2..bfbe48ad5 100644 --- a/internal/command/user_human_test.go +++ b/internal/command/user_human_test.go @@ -31,7 +31,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator id.Generator userPasswordAlg crypto.HashAlgorithm codeAlg crypto.EncryptionAlgorithm - newCode cryptoCodeFunc + newEmailCode func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) } type args struct { ctx context.Context @@ -446,7 +446,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - newCode: mockCode("emailCode", time.Hour), + newEmailCode: mockEmailCode("emailCode", time.Hour), }, args: args{ ctx: context.Background(), @@ -526,7 +526,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - newCode: mockCode("emailCode", time.Hour), + newEmailCode: mockEmailCode("emailCode", time.Hour), }, args: args{ ctx: context.Background(), @@ -1202,7 +1202,7 @@ func TestCommandSide_AddHuman(t *testing.T) { userPasswordAlg: tt.fields.userPasswordAlg, userEncryption: tt.fields.codeAlg, idGenerator: tt.fields.idGenerator, - newCode: tt.fields.newCode, + newEmailCode: tt.fields.newEmailCode, } err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail) if tt.res.err == nil { @@ -3992,3 +3992,18 @@ func TestAddHumanCommand(t *testing.T) { }) } } + +func mockEmailCode(code string, exp time.Duration) func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { + return &CryptoCodeWithExpiry{ + Crypted: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte(code), + }, + Plain: code, + Expiry: exp, + }, nil + } +} diff --git a/internal/command/user_human_webauthn.go b/internal/command/user_human_webauthn.go index 5156b5f8f..2d9d8db7a 100644 --- a/internal/command/user_human_webauthn.go +++ b/internal/command/user_human_webauthn.go @@ -539,7 +539,7 @@ func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, res } if !direct { codeEventCreator = func(ctx context.Context, agg *eventstore.Aggregate, id string, cryptoCode *crypto.CryptoValue, exp time.Duration) eventstore.Command { - return usr_repo.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, id, cryptoCode, exp, "", false) + return usr_repo.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, id, cryptoCode, exp) } } codeEvent := codeEventCreator(ctx, UserAggregateFromWriteModel(&initCode.WriteModel), codeID, cryptoCode, passwordlessCodeGenerator.Expiry()) diff --git a/internal/command/user_human_webauthn_model.go b/internal/command/user_human_webauthn_model.go index b92e35dbd..bbf61ee95 100644 --- a/internal/command/user_human_webauthn_model.go +++ b/internal/command/user_human_webauthn_model.go @@ -512,9 +512,6 @@ func (wm *HumanPasswordlessInitCodeWriteModel) appendRequestedEvent(e *user.Huma wm.CryptoCode = e.Code wm.Expiration = e.Expiry wm.State = domain.PasswordlessInitCodeStateRequested - if e.CodeReturned { - wm.State = domain.PasswordlessInitCodeStateActive - } } func (wm *HumanPasswordlessInitCodeWriteModel) appendCheckFailedEvent(e *user.HumanPasswordlessInitCodeCheckFailedEvent) { diff --git a/internal/command/user_v2_email_test.go b/internal/command/user_v2_email_test.go index dfcbe9107..af6478473 100644 --- a/internal/command/user_v2_email_test.go +++ b/internal/command/user_v2_email_test.go @@ -199,7 +199,7 @@ func TestCommands_ChangeUserEmailURLTemplate(t *testing.T) { email: "email-changed@test.ch", urlTmpl: "{{", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), }, { name: "permission missing", diff --git a/internal/command/user_v2_passkey.go b/internal/command/user_v2_passkey.go deleted file mode 100644 index 2795e6df8..000000000 --- a/internal/command/user_v2_passkey.go +++ /dev/null @@ -1,156 +0,0 @@ -package command - -import ( - "context" - "io" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/domain" - caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/user" -) - -// RegisterUserPasskey creates a passkey registration for the current authenticated user. -// UserID, ussualy taken from the request is compaired against the user ID in the context. -func (c *Commands) RegisterUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment) (*domain.PasskeyRegistrationDetails, error) { - if err := authz.UserIDInCTX(ctx, userID); err != nil { - return nil, err - } - return c.registerUserPasskey(ctx, userID, resourceOwner, authenticator) -} - -// RegisterUserPasskeyWithCode registers a new passkey for a unauthenticated user id. -// The resource is protected by the code, identified by the codeID. -func (c *Commands) RegisterUserPasskeyWithCode(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment, codeID, code string, alg crypto.EncryptionAlgorithm) (*domain.PasskeyRegistrationDetails, error) { - event, err := c.verifyUserPasskeyCode(ctx, userID, resourceOwner, codeID, code, alg) - if err != nil { - return nil, err - } - - return c.registerUserPasskey(ctx, userID, resourceOwner, authenticator, event) -} - -type eventCallback func(context.Context, *eventstore.Aggregate) eventstore.Command - -// verifyUserPasskeyCode verifies a passkey code, identified by codeID and userID. -// A code can only be used once. -// Upon success an event callback is returned, which must be called after -// all other events for the current request are created. -// This prevent consuming a code when another error occurred after verification. -func (c *Commands) verifyUserPasskeyCode(ctx context.Context, userID, resourceOwner, codeID, code string, alg crypto.EncryptionAlgorithm) (eventCallback, error) { - wm := NewHumanPasswordlessInitCodeWriteModel(userID, codeID, resourceOwner) - err := c.eventstore.FilterToQueryReducer(ctx, wm) - if err != nil { - return nil, err - } - err = verifyCryptoCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg, wm.ChangeDate, wm.Expiration, wm.CryptoCode, code) - if err != nil || wm.State != domain.PasswordlessInitCodeStateActive { - c.verifyUserPasskeyCodeFailed(ctx, wm) - return nil, caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid") - } - return func(ctx context.Context, userAgg *eventstore.Aggregate) eventstore.Command { - return user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, codeID) - }, nil -} - -func (c *Commands) verifyUserPasskeyCodeFailed(ctx context.Context, wm *HumanPasswordlessInitCodeWriteModel) { - userAgg := UserAggregateFromWriteModel(&wm.WriteModel) - _, err := c.eventstore.Push(ctx, user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, wm.CodeID)) - logging.WithFields("userID", userAgg.ID).OnError(err).Error("RegisterUserPasskeyWithCode push failed") -} - -func (c *Commands) registerUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment, events ...eventCallback) (*domain.PasskeyRegistrationDetails, error) { - wm, userAgg, webAuthN, err := c.createUserPasskey(ctx, userID, resourceOwner, authenticator) - if err != nil { - return nil, err - } - return c.pushUserPasskey(ctx, wm, userAgg, webAuthN, events...) -} - -func (c *Commands) createUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment) (*HumanWebAuthNWriteModel, *eventstore.Aggregate, *domain.WebAuthNToken, error) { - passwordlessTokens, err := c.getHumanPasswordlessTokens(ctx, userID, resourceOwner) - if err != nil { - return nil, nil, nil, err - } - return c.addHumanWebAuthN(ctx, userID, resourceOwner, false, passwordlessTokens, authenticator, domain.UserVerificationRequirementRequired) -} - -func (c *Commands) pushUserPasskey(ctx context.Context, wm *HumanWebAuthNWriteModel, userAgg *eventstore.Aggregate, webAuthN *domain.WebAuthNToken, events ...eventCallback) (*domain.PasskeyRegistrationDetails, error) { - cmds := make([]eventstore.Command, len(events)+1) - cmds[0] = user.NewHumanPasswordlessAddedEvent(ctx, userAgg, wm.WebauthNTokenID, webAuthN.Challenge) - for i, event := range events { - cmds[i+1] = event(ctx, userAgg) - } - - err := c.pushAppendAndReduce(ctx, wm, cmds...) - if err != nil { - return nil, err - } - return &domain.PasskeyRegistrationDetails{ - ObjectDetails: writeModelToObjectDetails(&wm.WriteModel), - PasskeyID: wm.WebauthNTokenID, - PublicKeyCredentialCreationOptions: webAuthN.CredentialCreationData, - }, nil -} - -// AddUserPasskeyCode generates a Passkey code and sends an email -// with the default generated URL (pointing to zitadel). -func (c *Commands) AddUserPasskeyCode(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm) (*domain.ObjectDetails, error) { - details, err := c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, "", false) - if err != nil { - return nil, err - } - return details.ObjectDetails, err -} - -// AddUserPasskeyCodeURLTemplate generates a Passkey code and sends an email -// with the URL created from passed template string. -// The template is executed as a test, before pushing to the eventstore. -func (c *Commands) AddUserPasskeyCodeURLTemplate(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm, urlTmpl string) (*domain.ObjectDetails, error) { - if err := domain.RenderPasskeyURLTemplate(io.Discard, urlTmpl, userID, resourceOwner, "codeID", "code"); err != nil { - return nil, err - } - details, err := c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, urlTmpl, false) - if err != nil { - return nil, err - } - return details.ObjectDetails, err -} - -// AddUserPasskeyCodeReturn generates and returns a Passkey code. -// No email will be send to the user. -func (c *Commands) AddUserPasskeyCodeReturn(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm) (*domain.PasskeyCodeDetails, error) { - return c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, "", true) -} - -func (c *Commands) addUserPasskeyCode(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm, urlTmpl string, returnCode bool) (*domain.PasskeyCodeDetails, error) { - codeID, err := c.idGenerator.Next() - if err != nil { - return nil, err - } - code, err := c.newCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) - if err != nil { - return nil, err - } - wm := NewHumanPasswordlessInitCodeWriteModel(userID, codeID, resourceOwner) - err = c.eventstore.FilterToQueryReducer(ctx, wm) - if err != nil { - return nil, err - } - agg := UserAggregateFromWriteModel(&wm.WriteModel) - - cmd := user.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, codeID, code.Crypted, code.Expiry, urlTmpl, returnCode) - err = c.pushAppendAndReduce(ctx, wm, cmd) - if err != nil { - return nil, err - } - return &domain.PasskeyCodeDetails{ - ObjectDetails: writeModelToObjectDetails(&wm.WriteModel), - CodeID: codeID, - Code: code.Plain, - }, nil -} diff --git a/internal/command/user_v2_passkey_test.go b/internal/command/user_v2_passkey_test.go deleted file mode 100644 index aefa9685a..000000000 --- a/internal/command/user_v2_passkey_test.go +++ /dev/null @@ -1,915 +0,0 @@ -package command - -import ( - "context" - "io" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/text/language" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/domain" - caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" - "github.com/zitadel/zitadel/internal/id" - id_mock "github.com/zitadel/zitadel/internal/id/mock" - "github.com/zitadel/zitadel/internal/repository/org" - "github.com/zitadel/zitadel/internal/repository/user" - webauthn_helper "github.com/zitadel/zitadel/internal/webauthn" -) - -func TestCommands_RegisterUserPasskey(t *testing.T) { - ctx := authz.NewMockContextWithPermissions("instance1", "org1", "user1", nil) - ctx = authz.WithRequestedDomain(ctx, "example.com") - - webauthnConfig := &webauthn_helper.Config{ - DisplayName: "test", - ExternalSecure: true, - } - userAgg := &user.NewAggregate("user1", "org1").Aggregate - type fields struct { - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - authenticator domain.AuthenticatorAttachment - } - tests := []struct { - name string - fields fields - args args - want *domain.PasskeyRegistrationDetails - wantErr error - }{ - { - name: "wrong user", - args: args{ - userID: "foo", - resourceOwner: "org1", - authenticator: domain.AuthenticatorAttachmentCrossPlattform, - }, - wantErr: caos_errs.ThrowUnauthenticated(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong"), - }, - { - name: "get human passwordless error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilterError(io.ErrClosedPipe), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - authenticator: domain.AuthenticatorAttachmentCrossPlattform, - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "id generator error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilter(), // getHumanPasswordlessTokens - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(ctx, - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectFilter(eventFromEventPusher( - org.NewOrgAddedEvent(ctx, - &org.NewAggregate("org1").Aggregate, - "org1", - ), - )), - expectFilter(eventFromEventPusher( - org.NewDomainPolicyAddedEvent(ctx, - &org.NewAggregate("org1").Aggregate, - false, false, false, - ), - )), - ), - idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - authenticator: domain.AuthenticatorAttachmentCrossPlattform, - }, - wantErr: io.ErrClosedPipe, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - webauthnConfig: webauthnConfig, - } - _, err := c.RegisterUserPasskey(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authenticator) - require.ErrorIs(t, err, tt.wantErr) - // successful case can't be tested due to random challenge. - }) - } -} - -func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) { - ctx := authz.WithRequestedDomain(context.Background(), "example.com") - webauthnConfig := &webauthn_helper.Config{ - DisplayName: "test", - ExternalSecure: true, - } - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - es := eventstoreExpect(t, - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - ) - code, err := newCryptoCodeWithExpiry(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) - require.NoError(t, err) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - type fields struct { - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - authenticator domain.AuthenticatorAttachment - codeID string - code string - } - tests := []struct { - name string - fields fields - args args - wantErr error - }{ - { - name: "code verification error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilter( - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, "123", code.Crypted, time.Minute, "", false, - ), - ), - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), - ), - ), - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectPush([]*repository.Event{eventFromEventPusher( - user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), - )}), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - authenticator: domain.AuthenticatorAttachmentCrossPlattform, - codeID: "123", - code: "wrong", - }, - wantErr: caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid"), - }, - { - name: "code verification ok, get human passwordless error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilter( - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, "123", code.Crypted, time.Minute, "", false, - ), - ), - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), - ), - ), - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectFilterError(io.ErrClosedPipe), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - authenticator: domain.AuthenticatorAttachmentCrossPlattform, - codeID: "123", - code: code.Plain, - }, - wantErr: io.ErrClosedPipe, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - webauthnConfig: webauthnConfig, - } - _, err := c.RegisterUserPasskeyWithCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authenticator, tt.args.codeID, tt.args.code, alg) - require.ErrorIs(t, err, tt.wantErr) - // successful case can't be tested due to random challenge. - }) - } -} - -func TestCommands_verifyUserPasskeyCode(t *testing.T) { - ctx := authz.WithRequestedDomain(context.Background(), "example.com") - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - es := eventstoreExpect(t, - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - ) - code, err := newCryptoCodeWithExpiry(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) - require.NoError(t, err) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - - type fields struct { - eventstore *eventstore.Eventstore - } - type args struct { - userID string - resourceOwner string - codeID string - code string - } - tests := []struct { - name string - fields fields - args args - want *user.HumanPasswordlessInitCodeCheckSucceededEvent - wantErr error - }{ - { - name: "filter error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilterError(io.ErrClosedPipe), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - codeID: "123", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "code verification error", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilter( - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, "123", code.Crypted, time.Minute, "", false, - ), - ), - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), - ), - ), - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectPush([]*repository.Event{eventFromEventPusher( - user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), - )}), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - codeID: "123", - code: "wrong", - }, - wantErr: caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid"), - }, - { - name: "success", - fields: fields{ - eventstore: eventstoreExpect(t, - expectFilter( - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, "123", code.Crypted, time.Minute, "", false, - ), - ), - eventFromEventPusherWithCreationDateNow( - user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), - ), - ), - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - ), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - codeID: "123", - code: code.Plain, - }, - want: user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - eventstore: tt.fields.eventstore, - } - got, err := c.verifyUserPasskeyCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.codeID, tt.args.code, alg) - require.ErrorIs(t, err, tt.wantErr) - if tt.wantErr == nil { - assert.Equal(t, tt.want, got(ctx, userAgg)) - } - }) - } -} - -func TestCommands_pushUserPasskey(t *testing.T) { - ctx := authz.WithRequestedDomain(context.Background(), "example.com") - webauthnConfig := &webauthn_helper.Config{ - DisplayName: "test", - ExternalSecure: true, - } - userAgg := &user.NewAggregate("user1", "org1").Aggregate - - prep := []expect{ - expectFilter(), // getHumanPasswordlessTokens - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(ctx, - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectFilter(eventFromEventPusher( - org.NewOrgAddedEvent(ctx, - &org.NewAggregate("org1").Aggregate, - "org1", - ), - )), - expectFilter(eventFromEventPusher( - org.NewDomainPolicyAddedEvent(ctx, - &org.NewAggregate("org1").Aggregate, - false, false, false, - ), - )), - expectFilter(eventFromEventPusher( - user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush( - ctx, &org.NewAggregate("org1").Aggregate, user.HumanPasswordlessTokenAddedType, - ), "111", "challenge"), - )), - } - - type args struct { - events []eventCallback - } - tests := []struct { - name string - expectPush func(challenge string) expect - args args - wantErr error - }{ - { - name: "push error", - expectPush: func(challenge string) expect { - return expectPushFailed(io.ErrClosedPipe, []*repository.Event{eventFromEventPusher( - user.NewHumanPasswordlessAddedEvent(ctx, - userAgg, "123", challenge, - ), - )}) - }, - args: args{}, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - expectPush: func(challenge string) expect { - return expectPush([]*repository.Event{eventFromEventPusher( - user.NewHumanPasswordlessAddedEvent(ctx, - userAgg, "123", challenge, - ), - )}) - }, - args: args{}, - }, - { - name: "initcode succeeded event", - expectPush: func(challenge string) expect { - return expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessAddedEvent(ctx, - userAgg, "123", challenge, - ), - ), - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), - ), - }) - }, - args: args{ - events: []eventCallback{func(ctx context.Context, userAgg *eventstore.Aggregate) eventstore.Command { - return user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123") - }}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - eventstore: eventstoreExpect(t, prep...), - webauthnConfig: webauthnConfig, - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - } - wm, userAgg, webAuthN, err := c.createUserPasskey(ctx, "user1", "org1", domain.AuthenticatorAttachmentCrossPlattform) - require.NoError(t, err) - - c.eventstore = eventstoreExpect(t, tt.expectPush(webAuthN.Challenge)) - - got, err := c.pushUserPasskey(ctx, wm, userAgg, webAuthN, tt.args.events...) - require.ErrorIs(t, err, tt.wantErr) - if tt.wantErr == nil { - assert.NotEmpty(t, got.PublicKeyCredentialCreationOptions) - assert.Equal(t, "123", got.PasskeyID) - assert.Equal(t, "org1", got.ObjectDetails.ResourceOwner) - } - }) - } -} - -func TestCommands_AddUserPasskeyCode(t *testing.T) { - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - type fields struct { - newCode cryptoCodeFunc - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - } - tests := []struct { - name string - fields fields - args args - want *domain.ObjectDetails - wantErr error - }{ - { - name: "id generator error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t), - idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - fields: fields{ - newCode: mockCode("passkey1", time.Minute), - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), - ), - }), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - want: &domain.ObjectDetails{ - ResourceOwner: "org1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - newCode: tt.fields.newCode, - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - } - got, err := c.AddUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) { - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - - type fields struct { - newCode cryptoCodeFunc - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - urlTmpl string - } - tests := []struct { - name string - fields fields - args args - want *domain.ObjectDetails - wantErr error - }{ - { - name: "template error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - urlTmpl: "{{", - }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), - }, - { - name: "id generator error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t), - idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - fields: fields{ - newCode: mockCode("passkey1", time.Minute), - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, - time.Minute, - "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - false, - ), - ), - }), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - }, - want: &domain.ObjectDetails{ - ResourceOwner: "org1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - newCode: tt.fields.newCode, - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - } - got, err := c.AddUserPasskeyCodeURLTemplate(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, tt.args.urlTmpl) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) { - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - type fields struct { - newCode cryptoCodeFunc - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - } - tests := []struct { - name string - fields fields - args args - want *domain.PasskeyCodeDetails - wantErr error - }{ - { - name: "id generator error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t), - idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - fields: fields{ - newCode: mockCode("passkey1", time.Minute), - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", true, - ), - ), - }), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - want: &domain.PasskeyCodeDetails{ - ObjectDetails: &domain.ObjectDetails{ - ResourceOwner: "org1", - }, - CodeID: "123", - Code: "passkey1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - newCode: tt.fields.newCode, - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - } - got, err := c.AddUserPasskeyCodeReturn(context.Background(), tt.args.userID, tt.args.resourceOwner, alg) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestCommands_addUserPasskeyCode(t *testing.T) { - alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) - userAgg := &user.NewAggregate("user1", "org1").Aggregate - type fields struct { - newCode cryptoCodeFunc - eventstore *eventstore.Eventstore - idGenerator id.Generator - } - type args struct { - userID string - resourceOwner string - } - tests := []struct { - name string - fields fields - args args - want *domain.PasskeyCodeDetails - wantErr error - }{ - { - name: "id generator error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t), - idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "crypto error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "filter query error", - fields: fields{ - newCode: newCryptoCodeWithExpiry, - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectFilterError(io.ErrClosedPipe), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "push error", - fields: fields{ - newCode: mockCode("passkey1", time.Minute), - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectPushFailed(io.ErrClosedPipe, []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), - ), - }), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - wantErr: io.ErrClosedPipe, - }, - { - name: "success", - fields: fields{ - newCode: mockCode("passkey1", time.Minute), - eventstore: eventstoreExpect(t, - expectFilter(eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - userAgg, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), - ), - }), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), - }, - args: args{ - userID: "user1", - resourceOwner: "org1", - }, - want: &domain.PasskeyCodeDetails{ - ObjectDetails: &domain.ObjectDetails{ - ResourceOwner: "org1", - }, - CodeID: "123", - Code: "passkey1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Commands{ - newCode: tt.fields.newCode, - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - } - got, err := c.addUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, "", false) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/internal/domain/human_email.go b/internal/domain/human_email.go index 86fd2f9e1..578c9fe22 100644 --- a/internal/domain/human_email.go +++ b/internal/domain/human_email.go @@ -4,10 +4,12 @@ import ( "io" "regexp" "strings" + "text/template" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" + caos_errs "github.com/zitadel/zitadel/internal/errors" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" ) @@ -71,8 +73,19 @@ type ConfirmURLData struct { OrgID string } -// RenderConfirmURLTemplate parses and renders tmpl. +// RenderConfirmURLTemplate parses and renders tmplStr. // userID, code and orgID are passed into the [ConfirmURLData]. -func RenderConfirmURLTemplate(w io.Writer, tmpl, userID, code, orgID string) error { - return renderURLTemplate(w, tmpl, &ConfirmURLData{userID, code, orgID}) +// "%s%s?userID=%s&code=%s&orgID=%s" +func RenderConfirmURLTemplate(w io.Writer, tmplStr, userID, code, orgID string) error { + tmpl, err := template.New("").Parse(tmplStr) + if err != nil { + return caos_errs.ThrowInvalidArgument(err, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate") + } + + data := &ConfirmURLData{userID, code, orgID} + if err = tmpl.Execute(w, data); err != nil { + return caos_errs.ThrowInvalidArgument(err, "USERv2-ohSi5", "Errors.User.Email.InvalidURLTemplate") + } + + return nil } diff --git a/internal/domain/human_email_test.go b/internal/domain/human_email_test.go index 4b30dd667..f77ba6d5c 100644 --- a/internal/domain/human_email_test.go +++ b/internal/domain/human_email_test.go @@ -81,10 +81,10 @@ func TestEmailValid(t *testing.T) { func TestRenderConfirmURLTemplate(t *testing.T) { type args struct { - tmpl string - userID string - code string - orgID string + tmplStr string + userID string + code string + orgID string } tests := []struct { name string @@ -95,30 +95,30 @@ func TestRenderConfirmURLTemplate(t *testing.T) { { name: "invalid template", args: args{ - tmpl: "{{", - userID: "user1", - code: "123", - orgID: "org1", + tmplStr: "{{", + userID: "user1", + code: "123", + orgID: "org1", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), }, { name: "execution error", args: args{ - tmpl: "{{.Foo}}", - userID: "user1", - code: "123", - orgID: "org1", + tmplStr: "{{.Foo}}", + userID: "user1", + code: "123", + orgID: "org1", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ohSi5", "Errors.User.Email.InvalidURLTemplate"), }, { name: "success", args: args{ - tmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", - userID: "user1", - code: "123", - orgID: "org1", + tmplStr: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + userID: "user1", + code: "123", + orgID: "org1", }, want: "https://example.com/email/verify?userID=user1&code=123&orgID=org1", }, @@ -126,7 +126,7 @@ func TestRenderConfirmURLTemplate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var w strings.Builder - err := RenderConfirmURLTemplate(&w, tt.args.tmpl, tt.args.userID, tt.args.code, tt.args.orgID) + err := RenderConfirmURLTemplate(&w, tt.args.tmplStr, tt.args.userID, tt.args.code, tt.args.orgID) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.want, w.String()) }) diff --git a/internal/domain/url_template.go b/internal/domain/url_template.go deleted file mode 100644 index 172de3496..000000000 --- a/internal/domain/url_template.go +++ /dev/null @@ -1,19 +0,0 @@ -package domain - -import ( - "io" - "text/template" - - caos_errs "github.com/zitadel/zitadel/internal/errors" -) - -func renderURLTemplate(w io.Writer, tmpl string, data any) error { - parsed, err := template.New("").Parse(tmpl) - if err != nil { - return caos_errs.ThrowInvalidArgument(err, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate") - } - if err = parsed.Execute(w, data); err != nil { - return caos_errs.ThrowInvalidArgument(err, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate") - } - return nil -} diff --git a/internal/domain/url_template_test.go b/internal/domain/url_template_test.go deleted file mode 100644 index 1140d1424..000000000 --- a/internal/domain/url_template_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package domain - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - caos_errs "github.com/zitadel/zitadel/internal/errors" -) - -func Test_renderURLTemplate(t *testing.T) { - type args struct { - tmpl string - data any - } - tests := []struct { - name string - args args - wantW string - wantErr error - }{ - { - name: "parse error", - args: args{ - tmpl: "{{", - }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), - }, - { - name: "execution error", - args: args{ - tmpl: "{{.Some}}", - data: struct{ Foo int }{Foo: 1}, - }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate"), - }, - { - name: "success", - args: args{ - tmpl: "{{.Foo}}", - data: struct{ Foo int }{Foo: 1}, - }, - wantW: "1", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - err := renderURLTemplate(w, tt.args.tmpl, tt.args.data) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.wantW, w.String()) - }) - } -} diff --git a/internal/domain/user_v2_passkey.go b/internal/domain/user_v2_passkey.go deleted file mode 100644 index 37f34097e..000000000 --- a/internal/domain/user_v2_passkey.go +++ /dev/null @@ -1,29 +0,0 @@ -package domain - -import "io" - -type PasskeyURLData struct { - UserID string - OrgID string - CodeID string - Code string -} - -// RenderPasskeyURLTemplate parses and renders tmpl. -// userID, orgID, codeID and code are passed into the [PasskeyURLData]. -func RenderPasskeyURLTemplate(w io.Writer, tmpl, userID, orgID, codeID, code string) error { - return renderURLTemplate(w, tmpl, &PasskeyURLData{userID, orgID, codeID, code}) -} - -type PasskeyCodeDetails struct { - *ObjectDetails - CodeID string - Code string -} - -type PasskeyRegistrationDetails struct { - *ObjectDetails - - PasskeyID string - PublicKeyCredentialCreationOptions []byte -} diff --git a/internal/domain/user_v2_passkey_test.go b/internal/domain/user_v2_passkey_test.go deleted file mode 100644 index 74cb1695e..000000000 --- a/internal/domain/user_v2_passkey_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package domain - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - caos_errs "github.com/zitadel/zitadel/internal/errors" -) - -func TestRenderPasskeyURLTemplate(t *testing.T) { - type args struct { - tmpl string - userID string - orgID string - codeID string - code string - } - tests := []struct { - name string - args args - wantW string - wantErr error - }{ - { - name: "parse error", - args: args{ - tmpl: "{{", - }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), - }, - { - name: "success", - args: args{ - tmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - userID: "user1", - orgID: "org1", - codeID: "99", - code: "123", - }, - wantW: "https://example.com/passkey/register?userID=user1&orgID=org1&codeID=99&code=123", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - err := RenderPasskeyURLTemplate(w, tt.args.tmpl, tt.args.userID, tt.args.orgID, tt.args.codeID, tt.args.code) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.wantW, w.String()) - }) - } -} diff --git a/internal/notification/handlers/usernotifier.go b/internal/notification/handlers/usernotifier.go index 5cd810236..10b0952ce 100644 --- a/internal/notification/handlers/usernotifier.go +++ b/internal/notification/handlers/usernotifier.go @@ -394,9 +394,6 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-EDtjd", "reduce.wrong.event.type %s", user.HumanPasswordlessInitCodeAddedType) } - if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil - } ctx := HandlerContext(event.Aggregate()) alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType) if err != nil { @@ -445,7 +442,7 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) ( e, u.metricSuccessfulDeliveriesEmail, u.metricFailedDeliveriesEmail, - ).SendPasswordlessRegistrationLink(notifyUser, origin, code, e.ID, e.URLTemplate) + ).SendPasswordlessRegistrationLink(notifyUser, origin, code, e.ID) if err != nil { return nil, err } diff --git a/internal/notification/types/email_verification_code_test.go b/internal/notification/types/email_verification_code_test.go index 71cebaca5..0e1975fbc 100644 --- a/internal/notification/types/email_verification_code_test.go +++ b/internal/notification/types/email_verification_code_test.go @@ -12,6 +12,27 @@ import ( ) func TestNotify_SendEmailVerificationCode(t *testing.T) { + type res struct { + url string + args map[string]interface{} + messageType string + allowUnverifiedNotificationChannel bool + } + notify := func(dst *res) Notify { + return func( + url string, + args map[string]interface{}, + messageType string, + allowUnverifiedNotificationChannel bool, + ) error { + dst.url = url + dst.args = args + dst.messageType = messageType + dst.allowUnverifiedNotificationChannel = allowUnverifiedNotificationChannel + return nil + } + } + type args struct { user *query.NotifyUser origin string @@ -21,7 +42,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { tests := []struct { name string args args - want *notifyResult + want *res wantErr error }{ { @@ -35,7 +56,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "", }, - want: ¬ifyResult{ + want: &res{ url: "https://example.com/ui/login/mail/verification?userID=user1&code=123&orgID=org1", args: map[string]interface{}{"Code": "123"}, messageType: domain.VerifyEmailMessageType, @@ -53,8 +74,8 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "{{", }, - want: ¬ifyResult{}, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + want: &res{}, + wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), }, { name: "template success", @@ -67,7 +88,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", }, - want: ¬ifyResult{ + want: &res{ url: "https://example.com/email/verify?userID=user1&code=123&orgID=org1", args: map[string]interface{}{"Code": "123"}, messageType: domain.VerifyEmailMessageType, @@ -77,8 +98,8 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, notify := mockNotify() - err := notify.SendEmailVerificationCode(tt.args.user, tt.args.origin, tt.args.code, tt.args.urlTmpl) + got := new(res) + err := notify(got).SendEmailVerificationCode(tt.args.user, tt.args.origin, tt.args.code, tt.args.urlTmpl) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.want, got) }) diff --git a/internal/notification/types/passwordless_registration_link.go b/internal/notification/types/passwordless_registration_link.go index 7f4ab0040..8c6f6894b 100644 --- a/internal/notification/types/passwordless_registration_link.go +++ b/internal/notification/types/passwordless_registration_link.go @@ -1,24 +1,12 @@ package types import ( - "strings" - "github.com/zitadel/zitadel/internal/api/ui/login" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" ) -func (notify Notify) SendPasswordlessRegistrationLink(user *query.NotifyUser, origin, code, codeID, urlTmpl string) error { - var url string - if urlTmpl == "" { - url = domain.PasswordlessInitCodeLink(origin+login.HandlerPrefix+login.EndpointPasswordlessRegistration, user.ID, user.ResourceOwner, codeID, code) - } else { - var buf strings.Builder - if err := domain.RenderPasskeyURLTemplate(&buf, urlTmpl, user.ID, user.ResourceOwner, codeID, code); err != nil { - return err - } - url = buf.String() - } - +func (notify Notify) SendPasswordlessRegistrationLink(user *query.NotifyUser, origin, code, codeID string) error { + url := domain.PasswordlessInitCodeLink(origin+login.HandlerPrefix+login.EndpointPasswordlessRegistration, user.ID, user.ResourceOwner, codeID, code) return notify(url, nil, domain.PasswordlessRegistrationMessageType, true) } diff --git a/internal/notification/types/passwordless_registration_link_test.go b/internal/notification/types/passwordless_registration_link_test.go deleted file mode 100644 index eb13b94a4..000000000 --- a/internal/notification/types/passwordless_registration_link_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/zitadel/zitadel/internal/domain" - caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/query" -) - -func TestNotify_SendPasswordlessRegistrationLink(t *testing.T) { - type args struct { - user *query.NotifyUser - origin string - code string - codeID string - urlTmpl string - } - tests := []struct { - name string - args args - want *notifyResult - wantErr error - }{ - { - name: "default URL", - args: args{ - user: &query.NotifyUser{ - ID: "user1", - ResourceOwner: "org1", - }, - origin: "https://example.com", - code: "123", - codeID: "456", - urlTmpl: "", - }, - want: ¬ifyResult{ - url: "https://example.com/ui/login/login/passwordless/init?userID=user1&orgID=org1&codeID=456&code=123", - messageType: domain.PasswordlessRegistrationMessageType, - allowUnverifiedNotificationChannel: true, - }, - }, - { - name: "template error", - args: args{ - user: &query.NotifyUser{ - ID: "user1", - ResourceOwner: "org1", - }, - origin: "https://example.com", - code: "123", - codeID: "456", - urlTmpl: "{{", - }, - want: ¬ifyResult{}, - wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), - }, - { - name: "template success", - args: args{ - user: &query.NotifyUser{ - ID: "user1", - ResourceOwner: "org1", - }, - origin: "https://example.com", - code: "123", - codeID: "456", - urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - }, - want: ¬ifyResult{ - url: "https://example.com/passkey/register?userID=user1&orgID=org1&codeID=456&code=123", - messageType: domain.PasswordlessRegistrationMessageType, - allowUnverifiedNotificationChannel: true, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, notify := mockNotify() - err := notify.SendPasswordlessRegistrationLink(tt.args.user, tt.args.origin, tt.args.code, tt.args.codeID, tt.args.urlTmpl) - require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/internal/notification/types/types_test.go b/internal/notification/types/types_test.go deleted file mode 100644 index 1b5066d19..000000000 --- a/internal/notification/types/types_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -type notifyResult struct { - url string - args map[string]interface{} - messageType string - allowUnverifiedNotificationChannel bool -} - -// mockNotify returns a notifyResult and Notify function for easy mocking. -// The notifyResult will only be populated after Notify is called. -func mockNotify() (*notifyResult, Notify) { - dst := new(notifyResult) - return dst, func(url string, args map[string]interface{}, messageType string, allowUnverifiedNotificationChannel bool) error { - *dst = notifyResult{ - url: url, - args: args, - messageType: messageType, - allowUnverifiedNotificationChannel: allowUnverifiedNotificationChannel, - } - return nil - } -} diff --git a/internal/repository/user/human_mfa_passwordless.go b/internal/repository/user/human_mfa_passwordless.go index f5e68b639..13889277f 100644 --- a/internal/repository/user/human_mfa_passwordless.go +++ b/internal/repository/user/human_mfa_passwordless.go @@ -315,11 +315,9 @@ func HumanPasswordlessInitCodeAddedEventMapper(event *repository.Event) (eventst type HumanPasswordlessInitCodeRequestedEvent struct { eventstore.BaseEvent `json:"-"` - ID string `json:"id"` - Code *crypto.CryptoValue `json:"code"` - Expiry time.Duration `json:"expiry"` - URLTemplate string `json:"url_template,omitempty"` - CodeReturned bool `json:"code_returned,omitempty"` + ID string `json:"id"` + Code *crypto.CryptoValue `json:"code"` + Expiry time.Duration `json:"expiry"` } func (e *HumanPasswordlessInitCodeRequestedEvent) Data() interface{} { @@ -336,8 +334,6 @@ func NewHumanPasswordlessInitCodeRequestedEvent( id string, code *crypto.CryptoValue, expiry time.Duration, - urlTmpl string, - codeReturned bool, ) *HumanPasswordlessInitCodeRequestedEvent { return &HumanPasswordlessInitCodeRequestedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -345,11 +341,9 @@ func NewHumanPasswordlessInitCodeRequestedEvent( aggregate, HumanPasswordlessInitCodeRequestedType, ), - ID: id, - Code: code, - Expiry: expiry, - URLTemplate: urlTmpl, - CodeReturned: codeReturned, + ID: id, + Code: code, + Expiry: expiry, } } diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 441b6be36..a0b5a1f89 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an UserIDMissing: User ID fehlt - UserIDWrong: "Der Anforderungsbenutzer ist nicht gleich dem authentifizierten Benutzer" DomainPolicyNil: Organisation Policy ist leer EmailAsUsernameNotAllowed: Benutzername darf keine E-Mail Adresse sein Invalid: Benutzerdaten sind ungültig @@ -68,7 +67,6 @@ Errors: NoChanges: Keine Änderungen gefunden InitCodeNotFound: Kein Initialisierungs-Code gefunden UsernameNotChanged: Benutzername wurde nicht verändert - InvalidURLTemplate: URL Template ist ungültig Profile: NotFound: Profil nicht gefunden NotChanged: Profil nicht verändert @@ -83,6 +81,7 @@ Errors: NotChanged: Email wurde nicht geändert Empty: Email ist leer IDMissing: Email ID fehlt + InvalidURLTemplate: URL Template ist ungültig Phone: NotFound: Telefonnummer nicht gefunden Invalid: Telefonnummer ist ungültig diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 276ad07da..5dfb9d5aa 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: User could not be found on chosen organization NotAllowedOrg: User is no member of the required organization UserIDMissing: User ID missing - UserIDWrong: "Request user not equal to authenticated user" DomainPolicyNil: Organisation Policy is empty EmailAsUsernameNotAllowed: Email is not allowed as username Invalid: Userdata is invalid @@ -68,7 +67,6 @@ Errors: NoChanges: No changes found InitCodeNotFound: Initialization Code not found UsernameNotChanged: Username not changed - InvalidURLTemplate: URL Template is invalid Profile: NotFound: Profile not found NotChanged: Profile not changed @@ -83,6 +81,7 @@ Errors: NotChanged: Email not changed Empty: Email is empty IDMissing: Email ID is missing + InvalidURLTemplate: URL Template is invalid Phone: NotFound: Phone not found Invalid: Phone is invalid diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index 757a73054..b40868ded 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: El usuario no pudo encontrarse en la organización elegida NotAllowedOrg: El usuario no es miembro de la organización requerida UserIDMissing: Falta el ID de usuario - UserIDWrong: "Solicitud de usuario no igual al usuario autenticado" DomainPolicyNil: Falta la política de la organización EmailAsUsernameNotAllowed: La dirección de Email no se permite como nombre de usuario Invalid: Los datos de usuario no son válidos @@ -68,7 +67,6 @@ Errors: NoChanges: No se encontraron cambios InitCodeNotFound: Código de inicialización no encontrado UsernameNotChanged: El nombre de usuario no cambió - InvalidURLTemplate: La plantilla URL no es válida Profile: NotFound: Perfil no encontrado NotChanged: El perfil no ha cambiado @@ -83,6 +81,7 @@ Errors: NotChanged: El email no ha cambiado Empty: El email no está vacío IDMissing: Falta el ID del email + InvalidURLTemplate: La plantilla URL no es válida Phone: NotFound: Teléfono no encontrado Invalid: El teléfono no es válido diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index eb5b5471e..2f3415f5a 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: L'utilisateur n'a pas été trouvé dans l'organisation choisie NotAllowedOrg: L'utilisateur n'est pas membre de l'organisation requise UserIDMissing: L'ID de l'utilisateur est manquant - UserIDWrong: "L'utilisateur de la demande n'est pas égal à l'utilisateur authentifié" DomainPolicyNil: La politique de l'organisation est vide EmailAsUsernameNotAllowed: L'email n'est pas autorisé comme nom d'utilisateur Invalid: Les données de l'utilisateur ne sont pas valides @@ -68,7 +67,6 @@ Errors: NoChanges: Aucun changement trouvé InitCodeNotFound: Code d'initialisation non trouvé UsernameNotChanged: Nom d'utilisateur non modifié - InvalidURLTemplate: Le modèle d'URL n'est pas valide Profile: NotFound: Profil non trouvé NotChanged: Le profil n'a pas changé @@ -83,6 +81,7 @@ Errors: NotChanged: L'adresse électronique n'a pas changé Empty: Email est vide IDMissing: Email ID manquant + InvalidURLTemplate: Le modèle d'URL n'est pas valide Phone: Notfound: Téléphone non trouvé Invalid: Le téléphone n'est pas valide diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 7ce4200b7..16aff8c8f 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: L'utente non è stato trovato nell'organizzazione scelta NotAllowedOrg: L'utente non è membro dell'organizzazione richiesta UserIDMissing: ID utente mancante - UserIDWrong: "Utente richiesta non uguale all'utente autenticato" DomainPolicyNil: Impostazione Org IAM mancante EmailAsUsernameNotAllowed: L'e-mail non è consentita come nome utente Invalid: I dati utente non sono validi @@ -68,7 +67,6 @@ Errors: NoChanges: Nessun cambiamento trovato InitCodeNotFound: Codice di inizializzazione non trovato UsernameNotChanged: Nome utente non cambiato - InvalidURLTemplate: Il modello di URL non è valido Profile: NotFound: Profilo non trovato NotChanged: Profilo non cambiato @@ -83,6 +81,7 @@ Errors: NotChanged: Email non cambiata Empty: Email è vuota IDMissing: Email ID mancante + InvalidURLTemplate: Il modello di URL non è valido Phone: NotFound: Telefono non trovato Invalid: Il telefono non è valido diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index 394880e11..a45a924a6 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: ユーザーが選択した組織内で見つかりません NotAllowedOrg: ユーザーが必要な組織のメンバーでありません UserIDMissing: ユーザーIDがありません - UserIDWrong: "リクエストユーザーが認証されたユーザーと等しくない" DomainPolicyNil: 組織ポリシーが空です EmailAsUsernameNotAllowed: メールアドレスはユーザー名として使用できません Invalid: 無効なユーザーデータです @@ -68,7 +67,6 @@ Errors: NoChanges: 変更は見つかりません InitCodeNotFound: 初期化コードが見つかりません UsernameNotChanged: ユーザー名は変更されていません - InvalidURLTemplate: URLテンプレートが無効です Profile: NotFound: プロファイルが見つかりません NotChanged: プロファイルが変更されていません @@ -78,6 +76,7 @@ Errors: Invalid: 無効なメールアドレスです AlreadyVerified: メールアドレスはすでに検証済みです NotChanged: メールアドレスが変更されていません + InvalidURLTemplate: URLテンプレートが無効です Phone: NotFound: 電話番号が見つかりません Invalid: 無効な電話番号です diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index 3be75368b..9e7131d8d 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: Użytkownik nie został znaleziony w wybranej organizacji NotAllowedOrg: Użytkownik nie jest członkiem wymaganej organizacji UserIDMissing: Brakuje ID użytkownika - UserIDWrong: "Żądanie użytkownika nie jest równe uwierzytelnionemu użytkownikowi" DomainPolicyNil: Polityka organizacji jest pusta EmailAsUsernameNotAllowed: Adres e-mail nie jest dozwolony jako nazwa użytkownika Invalid: Dane użytkownika są nieprawidłowe @@ -68,7 +67,6 @@ Errors: NoChanges: Nie znaleziono zmian InitCodeNotFound: Kod inicjalizacji nie znaleziony UsernameNotChanged: Nazwa użytkownika nie została zmieniona - InvalidURLTemplate: Szablon URL jest nieprawidłowy Profile: NotFound: Profil nie znaleziony NotChanged: Profil nie zmieniony @@ -83,6 +81,7 @@ Errors: NotChanged: Adres e-mail nie zmieniony Empty: Adres e-mail jest pusty IDMissing: Adres e-mail ID brakuje + InvalidURLTemplate: Szablon URL jest nieprawidłowy Phone: NotFound: Numer telefonu nie znaleziony Invalid: Numer telefonu jest nieprawidłowy diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index eb3a76c77..331c41db9 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -53,7 +53,6 @@ Errors: NotFoundOnOrg: 在所选组织中找不到用户 NotAllowedOrg: 用户不是所需组织的成员 UserIDMissing: 缺少用户 ID - UserIDWrong: "请求用户不等于经过身份验证的用户" DomainPolicyNil: 组织策略为空 EmailAsUsernameNotAllowed: 电子邮件不允许作为用户名 Invalid: 用户数据无效 @@ -68,7 +67,6 @@ Errors: NoChanges: 未发现任何更改 InitCodeNotFound: 未找到初始化验证码 UsernameNotChanged: 用户名未更改 - InvalidURLTemplate: URL模板无效 Profile: NotFound: 未找到个人资料 NotChanged: 个人资料未更改 @@ -83,6 +81,7 @@ Errors: NotChanged: 电子邮件未更改 Empty: 电子邮件是空的 IDMissing: 电子邮件ID丢失 + InvalidURLTemplate: URL模板无效 Phone: NotFound: 手机号码未找到 Invalid: 手机号码无效 diff --git a/internal/webauthn/client.go b/internal/webauthn/client.go deleted file mode 100644 index ac7fbe766..000000000 --- a/internal/webauthn/client.go +++ /dev/null @@ -1,36 +0,0 @@ -package webauthn - -import ( - "fmt" - - "github.com/descope/virtualwebauthn" -) - -type Client struct { - rp virtualwebauthn.RelyingParty - auth virtualwebauthn.Authenticator - credential virtualwebauthn.Credential -} - -func NewClient(name, domain, origin string) *Client { - rp := virtualwebauthn.RelyingParty{ - Name: name, - ID: domain, - Origin: origin, - } - return &Client{ - rp: rp, - auth: virtualwebauthn.NewAuthenticator(), - credential: virtualwebauthn.NewCredential(virtualwebauthn.KeyTypeEC2), - } -} - -func (c *Client) CreateAttestationResponse(options []byte) ([]byte, error) { - parsedAttestationOptions, err := virtualwebauthn.ParseAttestationOptions(string(options)) - if err != nil { - return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err) - } - return []byte(virtualwebauthn.CreateAttestationResponse( - c.rp, c.auth, c.credential, *parsedAttestationOptions, - )), nil -} diff --git a/internal/webauthn/converter.go b/internal/webauthn/converter.go index 9b49ec016..431980c82 100644 --- a/internal/webauthn/converter.go +++ b/internal/webauthn/converter.go @@ -1,9 +1,8 @@ package webauthn import ( - "github.com/go-webauthn/webauthn/protocol" - "github.com/go-webauthn/webauthn/webauthn" - + "github.com/duo-labs/webauthn/protocol" + "github.com/duo-labs/webauthn/webauthn" "github.com/zitadel/zitadel/internal/domain" ) diff --git a/internal/webauthn/webauthn.go b/internal/webauthn/webauthn.go index 5d2517bc4..d1a62819b 100644 --- a/internal/webauthn/webauthn.go +++ b/internal/webauthn/webauthn.go @@ -5,8 +5,8 @@ import ( "context" "encoding/json" - "github.com/go-webauthn/webauthn/protocol" - "github.com/go-webauthn/webauthn/webauthn" + "github.com/duo-labs/webauthn/protocol" + "github.com/duo-labs/webauthn/webauthn" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/api/authz" @@ -177,13 +177,9 @@ func (w *Config) FinishLogin(ctx context.Context, user *domain.Human, webAuthN * func (w *Config) serverFromContext(ctx context.Context) (*webauthn.WebAuthn, error) { instance := authz.GetInstance(ctx) - webAuthn, err := webauthn.New(&webauthn.Config{ + return webauthn.New(&webauthn.Config{ RPDisplayName: w.DisplayName, RPID: instance.RequestedDomain(), - RPOrigins: []string{http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure)}, + RPOrigin: http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure), }) - if err != nil { - return nil, caos_errs.ThrowInternal(err, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig") - } - return webAuthn, nil } diff --git a/internal/webauthn/webauthn_test.go b/internal/webauthn/webauthn_test.go deleted file mode 100644 index df05d34e9..000000000 --- a/internal/webauthn/webauthn_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package webauthn - -import ( - "context" - "testing" - - "github.com/go-webauthn/webauthn/webauthn" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/zitadel/zitadel/internal/api/authz" - caos_errs "github.com/zitadel/zitadel/internal/errors" -) - -func TestConfig_serverFromContext(t *testing.T) { - type args struct { - ctx context.Context - } - tests := []struct { - name string - args args - want *webauthn.WebAuthn - wantErr error - }{ - { - name: "webauthn error", - args: args{context.Background()}, - wantErr: caos_errs.ThrowInternal(nil, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig"), - }, - { - name: "success", - args: args{authz.WithRequestedDomain(context.Background(), "example.com")}, - want: &webauthn.WebAuthn{ - Config: &webauthn.Config{ - RPDisplayName: "DisplayName", - RPID: "example.com", - RPOrigins: []string{"https://example.com"}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &Config{ - DisplayName: "DisplayName", - ExternalSecure: true, - } - got, err := w.serverFromContext(tt.args.ctx) - require.ErrorIs(t, err, tt.wantErr) - if tt.want != nil { - require.NotNil(t, got) - assert.Equal(t, tt.want.Config.RPDisplayName, got.Config.RPDisplayName) - assert.Equal(t, tt.want.Config.RPID, got.Config.RPID) - assert.Equal(t, tt.want.Config.RPOrigins, got.Config.RPOrigins) - } - }) - } -} diff --git a/proto/zitadel/user/v2alpha/auth.proto b/proto/zitadel/user/v2alpha/auth.proto index 92ac47b5f..754877fd4 100644 --- a/proto/zitadel/user/v2alpha/auth.proto +++ b/proto/zitadel/user/v2alpha/auth.proto @@ -20,7 +20,7 @@ message SendPasskeyRegistrationLink { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1; max_length: 200; - example: "\"https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}\""; + example: "\"https://example.com/passkey/register?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}\""; description: "\"Optionally set a url_template, which will be used in the mail sent by ZITADEL to guide the user to your passkey registration page. If no template is set, the default ZITADEL url will be used.\"" } ]; From 7692d2cb8d89aae2fa7e55881fc1055f3392f32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Mon, 5 Jun 2023 23:30:10 +0200 Subject: [PATCH 12/76] preserve reused code --- internal/api/grpc/fields.go | 36 ++++++++++++++++++++++++++++++++++++ internal/command/command.go | 14 ++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 internal/api/grpc/fields.go diff --git a/internal/api/grpc/fields.go b/internal/api/grpc/fields.go new file mode 100644 index 000000000..f0c9d30a6 --- /dev/null +++ b/internal/api/grpc/fields.go @@ -0,0 +1,36 @@ +package grpc + +import ( + "testing" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +// AllFieldsSet recusively checks if all values in a message +// have a non-zero value. +func AllFieldsSet(t testing.TB, msg protoreflect.Message, ignoreTypes ...protoreflect.FullName) { + ignore := make(map[protoreflect.FullName]bool, len(ignoreTypes)) + for _, name := range ignoreTypes { + ignore[name] = true + } + + md := msg.Descriptor() + name := md.FullName() + if ignore[name] { + return + } + + fields := md.Fields() + + for i := 0; i < fields.Len(); i++ { + fd := fields.Get(i) + if !msg.Has(fd) { + t.Errorf("not all fields set in %q, missing %q", name, fd.Name()) + continue + } + + if fd.Kind() == protoreflect.MessageKind { + AllFieldsSet(t, msg.Get(fd).Message(), ignoreTypes...) + } + } +} diff --git a/internal/command/command.go b/internal/command/command.go index 28e869af2..1b72dd9e0 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -141,11 +141,21 @@ func StartCommands( return repo, nil } -func AppendAndReduce(object interface { +type AppendReducer interface { AppendEvents(...eventstore.Event) // TODO: Why is it allowed to return an error here? Reduce() error -}, events ...eventstore.Event) error { +} + +func (c *Commands) pushAppendAndReduce(ctx context.Context, object AppendReducer, cmds ...eventstore.Command) error { + events, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return err + } + return AppendAndReduce(object, events...) +} + +func AppendAndReduce(object AppendReducer, events ...eventstore.Event) error { object.AppendEvents(events...) return object.Reduce() } From 56e5d1a9a868e88f7b52be6ffe2d59a5d6dd2822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Tue, 6 Jun 2023 00:05:46 +0200 Subject: [PATCH 13/76] fix linting error --- internal/webauthn/converter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/webauthn/converter.go b/internal/webauthn/converter.go index 431980c82..924fbbb49 100644 --- a/internal/webauthn/converter.go +++ b/internal/webauthn/converter.go @@ -3,6 +3,7 @@ package webauthn import ( "github.com/duo-labs/webauthn/protocol" "github.com/duo-labs/webauthn/webauthn" + "github.com/zitadel/zitadel/internal/domain" ) From 9af066d53cde600de52c6d25daf2bb935eaaa75e Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 7 Jun 2023 18:30:19 +0200 Subject: [PATCH 14/76] fix(setup): steps 10 and 11 (#5987) * fix(step10): count amount of wrong_events manually * fix(step11): create index for better performance --- cmd/setup/10.go | 18 ++++++++++--- cmd/setup/10/10_count_wrong_events.sql | 1 + cmd/setup/10/10_empty_table.sql | 1 + cmd/setup/10/10_fill_table.sql | 2 -- cmd/setup/11.go | 36 +++++++++++++++++++++---- cmd/setup/11/11_fetch_events.sql | 13 ++++++++- cmd/setup/11/cockroach/create_index.sql | 8 ++++++ cmd/setup/11/cockroach/drop_index.sql | 1 + cmd/setup/11/postgres/create_index.sql | 4 +++ cmd/setup/11/postgres/drop_index.sql | 1 + cmd/setup/config.go | 2 +- cmd/setup/setup.go | 5 ++-- cmd/setup/steps.yaml | 4 +++ 13 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 cmd/setup/10/10_count_wrong_events.sql create mode 100644 cmd/setup/10/10_empty_table.sql create mode 100644 cmd/setup/11/cockroach/create_index.sql create mode 100644 cmd/setup/11/cockroach/drop_index.sql create mode 100644 cmd/setup/11/postgres/create_index.sql create mode 100644 cmd/setup/11/postgres/drop_index.sql diff --git a/cmd/setup/10.go b/cmd/setup/10.go index 1661ccf7d..a83113cb1 100644 --- a/cmd/setup/10.go +++ b/cmd/setup/10.go @@ -19,6 +19,10 @@ var ( correctCreationDate10FillTable string //go:embed 10/10_update.sql correctCreationDate10Update string + //go:embed 10/10_count_wrong_events.sql + correctCreationDate10CountWrongEvents string + //go:embed 10/10_empty_table.sql + correctCreationDate10Truncate string ) type CorrectCreationDate struct { @@ -31,7 +35,7 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { defer cancel() for { - var affected int64 + affected := int64(0) err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { if mig.dbClient.Type() == "cockroach" { if _, err := tx.Exec("SET experimental_enable_temp_tables=on"); err != nil { @@ -43,16 +47,24 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { return err } + _, err = tx.ExecContext(ctx, correctCreationDate10Truncate) + if err != nil { + return err + } _, err = tx.ExecContext(ctx, correctCreationDate10FillTable) if err != nil { return err } - res, err := tx.ExecContext(ctx, correctCreationDate10Update) + res := tx.QueryRowContext(ctx, correctCreationDate10CountWrongEvents) + if err := res.Scan(&affected); err != nil || affected == 0 { + return err + } + + _, err = tx.ExecContext(ctx, correctCreationDate10Update) if err != nil { return err } - affected, _ = res.RowsAffected() logging.WithFields("count", affected).Info("creation dates changed") return nil }) diff --git a/cmd/setup/10/10_count_wrong_events.sql b/cmd/setup/10/10_count_wrong_events.sql new file mode 100644 index 000000000..2bd0bd7c8 --- /dev/null +++ b/cmd/setup/10/10_count_wrong_events.sql @@ -0,0 +1 @@ +SELECT COUNT(*) FROM wrong_events \ No newline at end of file diff --git a/cmd/setup/10/10_empty_table.sql b/cmd/setup/10/10_empty_table.sql new file mode 100644 index 000000000..9139fbe18 --- /dev/null +++ b/cmd/setup/10/10_empty_table.sql @@ -0,0 +1 @@ +TRUNCATE wrong_events \ No newline at end of file diff --git a/cmd/setup/10/10_fill_table.sql b/cmd/setup/10/10_fill_table.sql index e62bbd839..b2ac9e307 100644 --- a/cmd/setup/10/10_fill_table.sql +++ b/cmd/setup/10/10_fill_table.sql @@ -1,5 +1,3 @@ -TRUNCATE wrong_events; - INSERT INTO wrong_events ( SELECT * FROM ( SELECT diff --git a/cmd/setup/11.go b/cmd/setup/11.go index 4126216da..544188d12 100644 --- a/cmd/setup/11.go +++ b/cmd/setup/11.go @@ -3,7 +3,7 @@ package setup import ( "context" "database/sql" - _ "embed" + "embed" "time" "github.com/cockroachdb/cockroach-go/v2/crdb" @@ -21,11 +21,18 @@ var ( fillCreatedAt string //go:embed 11/11_set_column.sql setCreatedAtDetails string + //go:embed 11/postgres/create_index.sql + //go:embed 11/cockroach/create_index.sql + createdAtIndexCreateStmt embed.FS + //go:embed 11/postgres/drop_index.sql + //go:embed 11/cockroach/drop_index.sql + createdAtIndexDropStmt embed.FS ) type AddEventCreatedAt struct { - step10 *CorrectCreationDate - dbClient *database.DB + BulkAmount int + step10 *CorrectCreationDate + dbClient *database.DB } func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { @@ -39,10 +46,19 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { return err } + createIndex, err := readStmt(createdAtIndexCreateStmt, "11", mig.dbClient.Type(), "create_index.sql") + if err != nil { + return err + } + _, err = mig.dbClient.ExecContext(ctx, createIndex) + if err != nil { + return err + } + for { var count int err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { - rows, err := tx.Query(fetchCreatedAt) + rows, err := tx.Query(fetchCreatedAt, mig.BulkAmount) if err != nil { return err } @@ -80,7 +96,7 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { if err != nil { return err } - logging.WithFields("count", count).Debug("creation dates set") + logging.WithFields("count", count).Info("creation dates set") if count < 20 { break } @@ -88,6 +104,16 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { logging.Info("set details") _, err = mig.dbClient.ExecContext(ctx, setCreatedAtDetails) + if err != nil { + return err + } + + dropIndex, err := readStmt(createdAtIndexDropStmt, "11", mig.dbClient.Type(), "drop_index.sql") + if err != nil { + return err + } + _, err = mig.dbClient.ExecContext(ctx, dropIndex) + return err } diff --git a/cmd/setup/11/11_fetch_events.sql b/cmd/setup/11/11_fetch_events.sql index 7c7512526..f66882675 100644 --- a/cmd/setup/11/11_fetch_events.sql +++ b/cmd/setup/11/11_fetch_events.sql @@ -1 +1,12 @@ -SELECT id, creation_date FROM eventstore.events WHERE created_at IS NULL ORDER BY event_sequence DESC, instance_id LIMIT 20 FOR UPDATE \ No newline at end of file +SELECT + id + , creation_date +FROM + eventstore.events +WHERE + created_at IS NULL +ORDER BY + event_sequence DESC + , instance_id +LIMIT $1 +FOR UPDATE \ No newline at end of file diff --git a/cmd/setup/11/cockroach/create_index.sql b/cmd/setup/11/cockroach/create_index.sql new file mode 100644 index 000000000..6fdd768a3 --- /dev/null +++ b/cmd/setup/11/cockroach/create_index.sql @@ -0,0 +1,8 @@ +CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events ( + event_sequence DESC + , instance_id +) STORING ( + id + , creation_date + , created_at +) WHERE created_at IS NULL; \ No newline at end of file diff --git a/cmd/setup/11/cockroach/drop_index.sql b/cmd/setup/11/cockroach/drop_index.sql new file mode 100644 index 000000000..da91018d7 --- /dev/null +++ b/cmd/setup/11/cockroach/drop_index.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS eventstore.events@ca_fill_idx; \ No newline at end of file diff --git a/cmd/setup/11/postgres/create_index.sql b/cmd/setup/11/postgres/create_index.sql new file mode 100644 index 000000000..a6fb37c99 --- /dev/null +++ b/cmd/setup/11/postgres/create_index.sql @@ -0,0 +1,4 @@ +CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events ( + event_sequence DESC + , instance_id +) WHERE created_at IS NULL; \ No newline at end of file diff --git a/cmd/setup/11/postgres/drop_index.sql b/cmd/setup/11/postgres/drop_index.sql new file mode 100644 index 000000000..acfe2f49b --- /dev/null +++ b/cmd/setup/11/postgres/drop_index.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS eventstore.ca_fill_idx; \ No newline at end of file diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 5e9819e4c..0d659e6e7 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -66,7 +66,7 @@ type Steps struct { s8AuthTokens *AuthTokenIndexes s9EventstoreIndexes2 *EventstoreIndexesNew CorrectCreationDate *CorrectCreationDate - s11AddEventCreatedAt *AddEventCreatedAt + AddEventCreatedAt *AddEventCreatedAt } type encryptionKeyConfig struct { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 59b7c5d6b..3f42503c2 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -91,7 +91,8 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s8AuthTokens = &AuthTokenIndexes{dbClient: dbClient} steps.s9EventstoreIndexes2 = New09(dbClient) steps.CorrectCreationDate.dbClient = dbClient - steps.s11AddEventCreatedAt = &AddEventCreatedAt{dbClient: dbClient, step10: steps.CorrectCreationDate} + steps.AddEventCreatedAt.dbClient = dbClient + steps.AddEventCreatedAt.step10 = steps.CorrectCreationDate err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -129,7 +130,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { logging.OnError(err).Fatal("unable to migrate step 9") err = migration.Migrate(ctx, eventstoreClient, steps.CorrectCreationDate) logging.OnError(err).Fatal("unable to migrate step 10") - err = migration.Migrate(ctx, eventstoreClient, steps.s11AddEventCreatedAt) + err = migration.Migrate(ctx, eventstoreClient, steps.AddEventCreatedAt) logging.OnError(err).Fatal("unable to migrate step 11") for _, repeatableStep := range repeatableSteps { diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index f5cb4b0c2..e495276e5 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -30,5 +30,9 @@ FirstInstance: MachineKey: ExpirationDate: Type: + CorrectCreationDate: FailAfter: 5m + +AddEventCreatedAt: + BulkAmount: 100 \ No newline at end of file From 18f761d404a48fe10c5207432e818358a3a2bf42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Thu, 8 Jun 2023 09:39:11 +0200 Subject: [PATCH 15/76] fix: re-apply register Passkey API v2 (#5990) Revert "fix: REVERT register Passkey API v2" --- cmd/defaults.yaml | 4 + go.mod | 12 +- go.sum | 363 +------ internal/api/authz/user.go | 16 + .../settings/v2/settings_converter_test.go | 43 +- internal/api/grpc/user/v2/passkey.go | 104 ++ .../grpc/user/v2/passkey_integration_test.go | 309 ++++++ internal/api/grpc/user/v2/passkey_test.go | 210 ++++ internal/command/command.go | 4 +- internal/command/crypto.go | 46 +- internal/command/crypto_test.go | 242 +++++ internal/command/email.go | 4 +- internal/command/user_human_test.go | 23 +- internal/command/user_human_webauthn.go | 2 +- internal/command/user_human_webauthn_model.go | 3 + internal/command/user_v2_email_test.go | 2 +- internal/command/user_v2_passkey.go | 156 +++ internal/command/user_v2_passkey_test.go | 915 ++++++++++++++++++ internal/domain/human_email.go | 19 +- internal/domain/human_email_test.go | 38 +- internal/domain/url_template.go | 19 + internal/domain/url_template_test.go | 56 ++ internal/domain/user_v2_passkey.go | 29 + internal/domain/user_v2_passkey_test.go | 54 ++ .../notification/handlers/usernotifier.go | 5 +- .../types/email_verification_code_test.go | 35 +- .../types/passwordless_registration_link.go | 16 +- .../passwordless_registration_link_test.go | 88 ++ internal/notification/types/types_test.go | 23 + .../repository/user/human_mfa_passwordless.go | 18 +- internal/static/i18n/de.yaml | 3 +- internal/static/i18n/en.yaml | 3 +- internal/static/i18n/es.yaml | 3 +- internal/static/i18n/fr.yaml | 3 +- internal/static/i18n/it.yaml | 3 +- internal/static/i18n/ja.yaml | 3 +- internal/static/i18n/pl.yaml | 3 +- internal/static/i18n/zh.yaml | 3 +- internal/webauthn/client.go | 36 + internal/webauthn/converter.go | 4 +- internal/webauthn/webauthn.go | 12 +- internal/webauthn/webauthn_test.go | 58 ++ proto/zitadel/user/v2alpha/auth.proto | 2 +- 43 files changed, 2479 insertions(+), 515 deletions(-) create mode 100644 internal/api/authz/user.go create mode 100644 internal/api/grpc/user/v2/passkey.go create mode 100644 internal/api/grpc/user/v2/passkey_integration_test.go create mode 100644 internal/api/grpc/user/v2/passkey_test.go create mode 100644 internal/command/crypto_test.go create mode 100644 internal/command/user_v2_passkey.go create mode 100644 internal/command/user_v2_passkey_test.go create mode 100644 internal/domain/url_template.go create mode 100644 internal/domain/url_template_test.go create mode 100644 internal/domain/user_v2_passkey.go create mode 100644 internal/domain/user_v2_passkey_test.go create mode 100644 internal/notification/types/passwordless_registration_link_test.go create mode 100644 internal/notification/types/types_test.go create mode 100644 internal/webauthn/client.go create mode 100644 internal/webauthn/webauthn_test.go diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 0867a1d5d..4cc990b34 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -735,6 +735,7 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" + - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" @@ -811,6 +812,7 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" + - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" @@ -847,6 +849,7 @@ InternalAuthZ: - "user.grant.write" - "user.grant.delete" - "user.membership.read" + - "user.passkey.write" - "project.read" - "project.member.read" - "project.role.read" @@ -882,6 +885,7 @@ InternalAuthZ: - "user.grant.delete" - "user.membership.read" - "user.credential.write" + - "user.passkey.write" - "policy.read" - "policy.write" - "policy.delete" diff --git a/go.mod b/go.mod index 102eda91a..eeca72a61 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,13 @@ require ( github.com/benbjohnson/clock v1.3.0 github.com/boombuler/barcode v1.0.1 github.com/cockroachdb/cockroach-go/v2 v2.3.3 + github.com/descope/virtualwebauthn v1.0.2 github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 github.com/drone/envsubst v1.0.3 - github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec github.com/envoyproxy/protoc-gen-validate v0.10.1 github.com/go-ldap/ldap/v3 v3.4.4 + github.com/go-webauthn/webauthn v0.8.2 github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 @@ -70,7 +71,7 @@ require ( go.opentelemetry.io/otel/sdk v1.14.0 go.opentelemetry.io/otel/sdk/metric v0.37.0 go.opentelemetry.io/otel/trace v1.14.0 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.8.0 golang.org/x/sync v0.1.0 @@ -86,12 +87,16 @@ require ( require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 // indirect - github.com/cloudflare/cfssl v1.6.3 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/go-webauthn/revoke v0.1.9 // indirect + github.com/google/go-tpm v0.3.3 // indirect github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/smartystreets/assertions v1.0.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect go.uber.org/multierr v1.11.0 // indirect ) @@ -136,7 +141,6 @@ require ( github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/certificate-transparency-go v1.1.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect diff --git a/go.sum b/go.sum index be8a76d22..716d80a78 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,6 @@ -bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= -bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -21,9 +17,6 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -38,7 +31,6 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc= cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= @@ -48,7 +40,6 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= @@ -59,21 +50,7 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= -contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= -contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= -contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= -contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= -contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= -contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= -github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= -github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= @@ -84,24 +61,18 @@ github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 h1:aRQEQ57Mw12h0tG8oo2UGC5d8fpUFCvD1lcS9fdGh6I= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0/go.mod h1:WzE/bKzbWw91rEv1w53y4taJheFSkUzp2Mu8uItorHg= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.37.0 h1:+3ioRuJPNQk6WkbdClWVo8FyfpXu/ZXePQ3syDwrN4o= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 h1:k5x4SiDgKS8wVQO/ww1fnoh5gwYEg6Wsi+1z5kB9uDM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0/go.mod h1:oEccMakRmMNrayCPR+5OmZE/aeXmTPzUtmomEXIPBdI= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= @@ -118,8 +89,6 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -131,32 +100,17 @@ github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc 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/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= -github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= -github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -167,24 +121,15 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= -github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -197,14 +142,9 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY= -github.com/cloudflare/cfssl v1.6.3 h1:hDhRaGQN55nh0510/7A5QBN3xLoDz/M7nQX80icXvzs= -github.com/cloudflare/cfssl v1.6.3/go.mod h1:Kq0iHKY8sm2klDeQ2Ci/FI+6QdBGuyPWodgTJFLrXIw= -github.com/cloudflare/redoctober v0.0.0-20201013214028-99c99a8e7544/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210322005330-6414d713912e/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -215,21 +155,14 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4= github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -238,19 +171,17 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/descope/virtualwebauthn v1.0.2 h1:cAvfS9wHh6On9HAE4Gjn3fJkf8MPQW2LzN8BPKEPs0M= +github.com/descope/virtualwebauthn v1.0.2/go.mod h1:iJvinjD1iZYqQ09J5lF0+795OdDbzTWcYQjPD/BF54M= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= @@ -295,62 +226,44 @@ github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4= github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo= github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= -github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec h1:darQ1FPPrwlzwmuN3fRMVCrsaCpuDqkKHADYzcMa73M= -github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec/go.mod h1:V3q8IgNpNqFio+56G0vy/QZIi7iho65UFrDwdF5OtZA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fullstorydev/grpcurl v1.8.0/go.mod h1:Mn2jWbdMrQGJQ8UD62uNyMumT2acsZUCkZIqFxsQf1o= -github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDePli4ecpK1N7gw= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -361,7 +274,6 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -383,15 +295,17 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= +github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= +github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= +github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= @@ -401,8 +315,6 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -412,8 +324,6 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -425,7 +335,6 @@ github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgR github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc h1:WkAZHSmcnJhZyutVoVXe7lDSQBbISxITcm57tYf22PE= github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -443,7 +352,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -461,7 +369,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -470,12 +377,6 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/certificate-transparency-go v1.1.2-0.20210422104406-9f33727a7a18/go.mod h1:6CKh9dscIRoqc2kC6YUFICHZMT9NrClyPrRVFrdw1QQ= -github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92/go.mod h1:kXWPsHVPSKVuxPPG69BRtumCbAW537FydV/GH89oBhM= -github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= -github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -490,16 +391,15 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= -github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= -github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= +github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= +github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= +github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= +github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= +github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= +github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE= -github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= @@ -513,27 +413,16 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk= github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/trillian v1.3.14-0.20210409160123-c5ea3abd4a41/go.mod h1:1dPv0CUjNQVFEDuAUFhZql16pw/VlPgaX8qj+g5pVzQ= -github.com/google/trillian v1.3.14-0.20210428093031-b4ddea2e86b1/go.mod h1:FdIJX+NoDk/dIN2ZxTyz5nAJWgf+NSSSriPAMThChTY= -github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= @@ -541,9 +430,6 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= -github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE= github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= @@ -565,15 +451,11 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= @@ -586,17 +468,13 @@ github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -613,18 +491,12 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= @@ -681,44 +553,33 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52/go.mod h1:RDZ+4PR3mDOtTpVbI0qBE+rdhmtIrtbssiNn38/1OWA= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= -github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= -github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k3a/html2text v1.1.0 h1:ks4hKSTdiTRsLr0DM771mI5TvsoG6zH7m1Ulv7eJRHw= @@ -727,15 +588,11 @@ github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 h1:K8qael4Lems github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw= github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 h1:2b9anKtyO/UTUQb+TAdPAW+w0p9xCIvng4fdZJ2xsYk= github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2 h1:k+lYMvS9cAl7e4Ea78qodfa6QZfXNa4QlFS/0GYpanI= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2/go.mod h1:PDdDH7RSKjjy9iFyoMzfeChOSmXpXuMEUqmAJSihxx4= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/kisom/goutils v1.4.3/go.mod h1:Lp5qrquG7yhYnWzZCI/68Pa/GpFynw//od6EkGnWpac= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= @@ -758,20 +615,16 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2KKkQWxfJUcU= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -779,44 +632,27 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= @@ -824,7 +660,6 @@ github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9 github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -836,8 +671,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -847,8 +680,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 h1:p4A2Jx7Lm3NV98VRMKlyWd3nqf8obft8NfXlAUmqd3I= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= @@ -861,8 +692,6 @@ github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -875,19 +704,13 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= -github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -897,15 +720,9 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= @@ -918,9 +735,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -931,9 +746,7 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -949,11 +762,8 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -962,21 +772,14 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= -github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -997,18 +800,13 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -1019,20 +817,14 @@ github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sony/sonyflake v1.1.0 h1:wnrEcL3aOkWmPlhScLEGAXKkLAIslnBteNUq4Bw6MM4= github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1041,8 +833,6 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -1054,10 +844,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -1066,7 +854,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1079,20 +866,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203/go.mod h1:0Xw5cYMOYpgaWs+OOSx41ugycl2qvKTi9tlMMcZhFyY= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w= github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4= @@ -1103,20 +882,11 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA= -github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= -github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -1134,26 +904,9 @@ github.com/zitadel/oidc/v2 v2.6.1 h1:NIW4/KPi9Q2Pae6MKEhrSl+DSC5W4574GlboHildroc github.com/zitadel/oidc/v2 v2.6.1/go.mod h1:vsFNrYCj2x0it0pYmIRVZ12HJ1VGaMVyGl7HLqw5p+Y= github.com/zitadel/saml v0.0.11 h1:kObucnBrcu1PHCO7RGT0iVeuJL/5I50gUgr40S41nMs= github.com/zitadel/saml v0.0.11/go.mod h1:YGWAvPZRv4DbEZ78Ht/2P0AWzGn+6WGhFf90PMXl0Po= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= -github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= -github.com/zmap/zcrypto v0.0.0-20210123152837-9cf5beac6d91/go.mod h1:R/deQh6+tSWlgI9tb4jNmXxn8nSCabl5ZQsBX9//I/E= -github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk= -github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= -go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= -go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= -go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0/go.mod h1:YPwSaBciV5G6Gpt435AasAG3ROetZsKNUzibRa/++oo= -go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY= -go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w= -go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= -go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0/go.mod h1:HnrHxjyCuZ8YDt8PYVyQQ5d1ZQfzJVEtQWllr5Vp/30= -go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i28snx8To= -go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1162,7 +915,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= @@ -1203,36 +955,26 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= -golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1240,10 +982,9 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1269,7 +1010,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1289,7 +1029,6 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1303,14 +1042,10 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1330,12 +1065,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1345,9 +1077,7 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1355,10 +1085,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= @@ -1366,7 +1092,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1387,7 +1112,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1396,14 +1120,12 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1427,25 +1149,17 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1483,19 +1197,15 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1503,19 +1213,15 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191118222007-07fc4c7f2b98/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1533,19 +1239,15 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1567,12 +1269,9 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -1588,32 +1287,23 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= google.golang.org/api v0.115.0 h1:6FFkVvStt4YqXSx3azKyzj7fXerGnVlLJ/eud01nBDE= google.golang.org/api v0.115.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1647,19 +1337,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210331142528-b7513248f0ba/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU= google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1684,13 +1365,10 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1701,7 +1379,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -1714,20 +1391,15 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= 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= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1754,16 +1426,13 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/internal/api/authz/user.go b/internal/api/authz/user.go new file mode 100644 index 000000000..13dc4076f --- /dev/null +++ b/internal/api/authz/user.go @@ -0,0 +1,16 @@ +package authz + +import ( + "context" + + "github.com/zitadel/zitadel/internal/errors" +) + +// UserIDInCTX checks if the userID +// equals the authenticated user in the context. +func UserIDInCTX(ctx context.Context, userID string) error { + if GetCtxData(ctx).UserID != userID { + return errors.ThrowUnauthenticated(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong") + } + return nil +} diff --git a/internal/api/grpc/settings/v2/settings_converter_test.go b/internal/api/grpc/settings/v2/settings_converter_test.go index 818bf64ff..11ebc0f71 100644 --- a/internal/api/grpc/settings/v2/settings_converter_test.go +++ b/internal/api/grpc/settings/v2/settings_converter_test.go @@ -11,38 +11,13 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/durationpb" + "github.com/zitadel/zitadel/internal/api/grpc" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha" ) -var ignoreMessageTypes = map[protoreflect.FullName]bool{ - "google.protobuf.Duration": true, -} - -// allFieldsSet recusively checks if all values in a message -// have a non-zero value. -func allFieldsSet(t testing.TB, msg protoreflect.Message) { - md := msg.Descriptor() - name := md.FullName() - if ignoreMessageTypes[name] { - return - } - - fields := md.Fields() - - for i := 0; i < fields.Len(); i++ { - fd := fields.Get(i) - if !msg.Has(fd) { - t.Errorf("not all fields set in %q, missing %q", name, fd.Name()) - continue - } - - if fd.Kind() == protoreflect.MessageKind { - allFieldsSet(t, msg.Get(fd).Message()) - } - } -} +var ignoreTypes = []protoreflect.FullName{"google.protobuf.Duration"} func Test_loginSettingsToPb(t *testing.T) { arg := &query.LoginPolicy{ @@ -100,7 +75,7 @@ func Test_loginSettingsToPb(t *testing.T) { } got := loginSettingsToPb(arg) - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("loginSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -241,7 +216,7 @@ func Test_passwordSettingsToPb(t *testing.T) { } got := passwordSettingsToPb(arg) - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("passwordSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -295,7 +270,7 @@ func Test_brandingSettingsToPb(t *testing.T) { } got := brandingSettingsToPb(arg, "http://example.com") - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("brandingSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -315,7 +290,7 @@ func Test_domainSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := domainSettingsToPb(arg) - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("domainSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -337,7 +312,7 @@ func Test_legalSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := legalAndSupportSettingsToPb(arg) - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("legalSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -353,7 +328,7 @@ func Test_lockoutSettingsToPb(t *testing.T) { ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE, } got := lockoutSettingsToPb(arg) - allFieldsSet(t, got.ProtoReflect()) + grpc.AllFieldsSet(t, got.ProtoReflect(), ignoreTypes...) if !proto.Equal(got, want) { t.Errorf("lockoutSettingsToPb() =\n%v\nwant\n%v", got, want) } @@ -387,7 +362,7 @@ func Test_identityProvidersToPb(t *testing.T) { got := identityProvidersToPb(arg) require.Len(t, got, len(got)) for i, v := range got { - allFieldsSet(t, v.ProtoReflect()) + grpc.AllFieldsSet(t, v.ProtoReflect(), ignoreTypes...) if !proto.Equal(v, want[i]) { t.Errorf("identityProvidersToPb() =\n%v\nwant\n%v", got, want) } diff --git a/internal/api/grpc/user/v2/passkey.go b/internal/api/grpc/user/v2/passkey.go new file mode 100644 index 000000000..eb1b276f8 --- /dev/null +++ b/internal/api/grpc/user/v2/passkey.go @@ -0,0 +1,104 @@ +package user + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/grpc/object/v2" + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" + user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +func (s *Server) RegisterPasskey(ctx context.Context, req *user.RegisterPasskeyRequest) (resp *user.RegisterPasskeyResponse, err error) { + var ( + resourceOwner = authz.GetCtxData(ctx).ResourceOwner + authenticator = passkeyAuthenticatorToDomain(req.GetAuthenticator()) + ) + if code := req.GetCode(); code != nil { + return passkeyRegistrationDetailsToPb( + s.command.RegisterUserPasskeyWithCode(ctx, req.GetUserId(), resourceOwner, authenticator, code.Id, code.Code, s.userCodeAlg), + ) + } + return passkeyRegistrationDetailsToPb( + s.command.RegisterUserPasskey(ctx, req.GetUserId(), resourceOwner, authenticator), + ) +} + +func passkeyAuthenticatorToDomain(pa user.PasskeyAuthenticator) domain.AuthenticatorAttachment { + switch pa { + case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_UNSPECIFIED: + return domain.AuthenticatorAttachmentUnspecified + case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM: + return domain.AuthenticatorAttachmentPlattform + case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM: + return domain.AuthenticatorAttachmentCrossPlattform + default: + return domain.AuthenticatorAttachmentUnspecified + } +} + +func passkeyRegistrationDetailsToPb(details *domain.PasskeyRegistrationDetails, err error) (*user.RegisterPasskeyResponse, error) { + if err != nil { + return nil, err + } + return &user.RegisterPasskeyResponse{ + Details: object.DomainToDetailsPb(details.ObjectDetails), + PasskeyId: details.PasskeyID, + PublicKeyCredentialCreationOptions: details.PublicKeyCredentialCreationOptions, + }, nil +} + +func (s *Server) VerifyPasskeyRegistration(ctx context.Context, req *user.VerifyPasskeyRegistrationRequest) (*user.VerifyPasskeyRegistrationResponse, error) { + resourceOwner := authz.GetCtxData(ctx).ResourceOwner + objectDetails, err := s.command.HumanHumanPasswordlessSetup(ctx, req.GetUserId(), resourceOwner, req.GetPasskeyName(), "", req.GetPublicKeyCredential()) + if err != nil { + return nil, err + } + return &user.VerifyPasskeyRegistrationResponse{ + Details: object.DomainToDetailsPb(objectDetails), + }, nil +} + +func (s *Server) CreatePasskeyRegistrationLink(ctx context.Context, req *user.CreatePasskeyRegistrationLinkRequest) (resp *user.CreatePasskeyRegistrationLinkResponse, err error) { + resourceOwner := authz.GetCtxData(ctx).ResourceOwner + + switch medium := req.Medium.(type) { + case nil: + return passkeyDetailsToPb( + s.command.AddUserPasskeyCode(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), + ) + case *user.CreatePasskeyRegistrationLinkRequest_SendLink: + return passkeyDetailsToPb( + s.command.AddUserPasskeyCodeURLTemplate(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg, medium.SendLink.GetUrlTemplate()), + ) + case *user.CreatePasskeyRegistrationLinkRequest_ReturnCode: + return passkeyCodeDetailsToPb( + s.command.AddUserPasskeyCodeReturn(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), + ) + default: + return nil, caos_errs.ThrowUnimplementedf(nil, "USERv2-gaD8y", "verification oneOf %T in method CreatePasskeyRegistrationLink not implemented", medium) + } +} + +func passkeyDetailsToPb(details *domain.ObjectDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { + if err != nil { + return nil, err + } + return &user.CreatePasskeyRegistrationLinkResponse{ + Details: object.DomainToDetailsPb(details), + }, nil +} + +func passkeyCodeDetailsToPb(details *domain.PasskeyCodeDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { + if err != nil { + return nil, err + } + return &user.CreatePasskeyRegistrationLinkResponse{ + Details: object.DomainToDetailsPb(details.ObjectDetails), + Code: &user.PasskeyRegistrationCode{ + Id: details.CodeID, + Code: details.Code, + }, + }, nil +} diff --git a/internal/api/grpc/user/v2/passkey_integration_test.go b/internal/api/grpc/user/v2/passkey_integration_test.go new file mode 100644 index 000000000..623b2544f --- /dev/null +++ b/internal/api/grpc/user/v2/passkey_integration_test.go @@ -0,0 +1,309 @@ +//go:build integration + +package user_test + +import ( + "context" + "testing" + + "github.com/muhlemmer/gu" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/webauthn" + object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha" + user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +func TestServer_RegisterPasskey(t *testing.T) { + userID := createHumanUser(t).GetUserId() + reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, + }) + require.NoError(t, err) + client := webauthn.NewClient(Tester.Config.WebAuthNName, Tester.Config.ExternalDomain, "https://"+Tester.Host()) + + type args struct { + ctx context.Context + req *user.RegisterPasskeyRequest + } + tests := []struct { + name string + args args + want *user.RegisterPasskeyResponse + wantErr bool + }{ + { + name: "missing user id", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{}, + }, + wantErr: true, + }, + { + name: "register code", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{ + UserId: userID, + Code: reg.GetCode(), + Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, + }, + }, + want: &user.RegisterPasskeyResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + }, + { + name: "reuse code (not allowed)", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{ + UserId: userID, + Code: reg.GetCode(), + Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, + }, + }, + wantErr: true, + }, + { + name: "wrong code", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{ + UserId: userID, + Code: &user.PasskeyRegistrationCode{ + Id: reg.GetCode().GetId(), + Code: "foobar", + }, + Authenticator: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM, + }, + }, + wantErr: true, + }, + { + name: "user mismatch", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{ + UserId: userID, + }, + }, + wantErr: true, + }, + /* TODO after we are able to obtain a Bearer token for a human user + { + name: "human user", + args: args{ + ctx: CTX, + req: &user.RegisterPasskeyRequest{ + UserId: humanUserID, + }, + }, + want: &user.RegisterPasskeyResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + }, + */ + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Client.RegisterPasskey(tt.args.ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotNil(t, got) + integration.AssertDetails(t, tt.want, got) + if tt.want != nil { + assert.NotEmpty(t, got.GetPasskeyId()) + assert.NotEmpty(t, got.GetPublicKeyCredentialCreationOptions()) + _, err := client.CreateAttestationResponse(got.GetPublicKeyCredentialCreationOptions()) + require.NoError(t, err) + } + }) + } +} + +func TestServer_VerifyPasskeyRegistration(t *testing.T) { + userID := createHumanUser(t).GetUserId() + reg, err := Client.CreatePasskeyRegistrationLink(CTX, &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, + }) + require.NoError(t, err) + pkr, err := Client.RegisterPasskey(CTX, &user.RegisterPasskeyRequest{ + UserId: userID, + Code: reg.GetCode(), + }) + require.NoError(t, err) + require.NotEmpty(t, pkr.GetPasskeyId()) + require.NotEmpty(t, pkr.GetPublicKeyCredentialCreationOptions()) + + client := webauthn.NewClient(Tester.Config.WebAuthNName, Tester.Config.ExternalDomain, "https://"+Tester.Host()) + attestationResponse, err := client.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions()) + require.NoError(t, err) + + type args struct { + ctx context.Context + req *user.VerifyPasskeyRegistrationRequest + } + tests := []struct { + name string + args args + want *user.VerifyPasskeyRegistrationResponse + wantErr bool + }{ + { + name: "missing user id", + args: args{ + ctx: CTX, + req: &user.VerifyPasskeyRegistrationRequest{ + PasskeyId: pkr.GetPasskeyId(), + PublicKeyCredential: []byte(attestationResponse), + PasskeyName: "nice name", + }, + }, + wantErr: true, + }, + { + name: "success", + args: args{ + ctx: CTX, + req: &user.VerifyPasskeyRegistrationRequest{ + UserId: userID, + PasskeyId: pkr.GetPasskeyId(), + PublicKeyCredential: attestationResponse, + PasskeyName: "nice name", + }, + }, + want: &user.VerifyPasskeyRegistrationResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + }, + { + name: "wrong credential", + args: args{ + ctx: CTX, + req: &user.VerifyPasskeyRegistrationRequest{ + UserId: userID, + PasskeyId: pkr.GetPasskeyId(), + PublicKeyCredential: []byte("attestationResponseattestationResponseattestationResponse"), + PasskeyName: "nice name", + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Client.VerifyPasskeyRegistration(tt.args.ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotNil(t, got) + integration.AssertDetails(t, tt.want, got) + }) + } +} + +func TestServer_CreatePasskeyRegistrationLink(t *testing.T) { + userID := createHumanUser(t).GetUserId() + + type args struct { + ctx context.Context + req *user.CreatePasskeyRegistrationLinkRequest + } + tests := []struct { + name string + args args + want *user.CreatePasskeyRegistrationLinkResponse + wantCode bool + wantErr bool + }{ + { + name: "missing user id", + args: args{ + ctx: CTX, + req: &user.CreatePasskeyRegistrationLinkRequest{}, + }, + wantErr: true, + }, + { + name: "send default mail", + args: args{ + ctx: CTX, + req: &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + }, + }, + want: &user.CreatePasskeyRegistrationLinkResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + }, + { + name: "send custom url", + args: args{ + ctx: CTX, + req: &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + Medium: &user.CreatePasskeyRegistrationLinkRequest_SendLink{ + SendLink: &user.SendPasskeyRegistrationLink{ + UrlTemplate: gu.Ptr("https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}"), + }, + }, + }, + }, + want: &user.CreatePasskeyRegistrationLinkResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + }, + { + name: "return code", + args: args{ + ctx: CTX, + req: &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, + }, + }, + want: &user.CreatePasskeyRegistrationLinkResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + wantCode: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Client.CreatePasskeyRegistrationLink(tt.args.ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotNil(t, got) + integration.AssertDetails(t, tt.want, got) + if tt.wantCode { + assert.NotEmpty(t, got.GetCode().GetId()) + assert.NotEmpty(t, got.GetCode().GetId()) + } + }) + } +} diff --git a/internal/api/grpc/user/v2/passkey_test.go b/internal/api/grpc/user/v2/passkey_test.go new file mode 100644 index 000000000..d1934c3ca --- /dev/null +++ b/internal/api/grpc/user/v2/passkey_test.go @@ -0,0 +1,210 @@ +package user + +import ( + "io" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/zitadel/internal/api/grpc" + "github.com/zitadel/zitadel/internal/domain" + object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha" + user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +func Test_passkeyAuthenticatorToDomain(t *testing.T) { + tests := []struct { + pa user.PasskeyAuthenticator + want domain.AuthenticatorAttachment + }{ + { + pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_UNSPECIFIED, + want: domain.AuthenticatorAttachmentUnspecified, + }, + { + pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM, + want: domain.AuthenticatorAttachmentPlattform, + }, + { + pa: user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM, + want: domain.AuthenticatorAttachmentCrossPlattform, + }, + { + pa: 999, + want: domain.AuthenticatorAttachmentUnspecified, + }, + } + for _, tt := range tests { + t.Run(tt.pa.String(), func(t *testing.T) { + got := passkeyAuthenticatorToDomain(tt.pa) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_passkeyRegistrationDetailsToPb(t *testing.T) { + type args struct { + details *domain.PasskeyRegistrationDetails + err error + } + tests := []struct { + name string + args args + want *user.RegisterPasskeyResponse + }{ + { + name: "an error", + args: args{ + details: nil, + err: io.ErrClosedPipe, + }, + }, + { + name: "ok", + args: args{ + details: &domain.PasskeyRegistrationDetails{ + ObjectDetails: &domain.ObjectDetails{ + Sequence: 22, + EventDate: time.Unix(3000, 22), + ResourceOwner: "me", + }, + PasskeyID: "123", + PublicKeyCredentialCreationOptions: []byte{1, 2, 3}, + }, + err: nil, + }, + want: &user.RegisterPasskeyResponse{ + Details: &object.Details{ + Sequence: 22, + ChangeDate: ×tamppb.Timestamp{ + Seconds: 3000, + Nanos: 22, + }, + ResourceOwner: "me", + }, + PasskeyId: "123", + PublicKeyCredentialCreationOptions: []byte{1, 2, 3}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := passkeyRegistrationDetailsToPb(tt.args.details, tt.args.err) + require.ErrorIs(t, err, tt.args.err) + assert.Equal(t, tt.want, got) + if tt.want != nil { + grpc.AllFieldsSet(t, got.ProtoReflect()) + } + }) + } +} + +func Test_passkeyDetailsToPb(t *testing.T) { + type args struct { + details *domain.ObjectDetails + err error + } + tests := []struct { + name string + args args + want *user.CreatePasskeyRegistrationLinkResponse + }{ + { + name: "an error", + args: args{ + details: nil, + err: io.ErrClosedPipe, + }, + }, + { + name: "ok", + args: args{ + details: &domain.ObjectDetails{ + Sequence: 22, + EventDate: time.Unix(3000, 22), + ResourceOwner: "me", + }, + err: nil, + }, + want: &user.CreatePasskeyRegistrationLinkResponse{ + Details: &object.Details{ + Sequence: 22, + ChangeDate: ×tamppb.Timestamp{ + Seconds: 3000, + Nanos: 22, + }, + ResourceOwner: "me", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := passkeyDetailsToPb(tt.args.details, tt.args.err) + require.ErrorIs(t, err, tt.args.err) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_passkeyCodeDetailsToPb(t *testing.T) { + type args struct { + details *domain.PasskeyCodeDetails + err error + } + tests := []struct { + name string + args args + want *user.CreatePasskeyRegistrationLinkResponse + }{ + { + name: "an error", + args: args{ + details: nil, + err: io.ErrClosedPipe, + }, + }, + { + name: "ok", + args: args{ + details: &domain.PasskeyCodeDetails{ + ObjectDetails: &domain.ObjectDetails{ + Sequence: 22, + EventDate: time.Unix(3000, 22), + ResourceOwner: "me", + }, + CodeID: "123", + Code: "456", + }, + err: nil, + }, + want: &user.CreatePasskeyRegistrationLinkResponse{ + Details: &object.Details{ + Sequence: 22, + ChangeDate: ×tamppb.Timestamp{ + Seconds: 3000, + Nanos: 22, + }, + ResourceOwner: "me", + }, + Code: &user.PasskeyRegistrationCode{ + Id: "123", + Code: "456", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := passkeyCodeDetailsToPb(tt.args.details, tt.args.err) + require.ErrorIs(t, err, tt.args.err) + assert.Equal(t, tt.want, got) + if tt.want != nil { + grpc.AllFieldsSet(t, got.ProtoReflect()) + } + }) + } +} diff --git a/internal/command/command.go b/internal/command/command.go index 1b72dd9e0..3c804576b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -32,7 +32,7 @@ type Commands struct { httpClient *http.Client checkPermission domain.PermissionCheck - newEmailCode func(ctx context.Context, filter preparation.FilterToQueryReducer, codeAlg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) + newCode cryptoCodeFunc eventstore *eventstore.Eventstore static static.Storage @@ -109,7 +109,7 @@ func StartCommands( webauthnConfig: webAuthN, httpClient: httpClient, checkPermission: permissionCheck, - newEmailCode: newEmailCode, + newCode: newCryptoCodeWithExpiry, sessionTokenCreator: sessionTokenCreator(idGenerator, sessionAlg), sessionTokenVerifier: sessionTokenVerifier, } diff --git a/internal/command/crypto.go b/internal/command/crypto.go index 404b2221f..e94f66463 100644 --- a/internal/command/crypto.go +++ b/internal/command/crypto.go @@ -10,6 +10,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" ) +type cryptoCodeFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) + type CryptoCodeWithExpiry struct { Crypted *crypto.CryptoValue Plain string @@ -17,42 +19,50 @@ type CryptoCodeWithExpiry struct { } func newCryptoCodeWithExpiry(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) { - config, err := secretGeneratorConfig(ctx, filter, typ) + gen, config, err := secretGenerator(ctx, filter, typ, alg) if err != nil { return nil, err } - - code := new(CryptoCodeWithExpiry) - switch a := alg.(type) { - case crypto.HashAlgorithm: - code.Crypted, code.Plain, err = crypto.NewCode(crypto.NewHashGenerator(*config, a)) - case crypto.EncryptionAlgorithm: - code.Crypted, code.Plain, err = crypto.NewCode(crypto.NewEncryptionGenerator(*config, a)) - default: - return nil, errors.ThrowInternal(nil, "COMMA-RreV6", "Errors.Internal") - } + crypted, plain, err := crypto.NewCode(gen) if err != nil { return nil, err } + return &CryptoCodeWithExpiry{ + Crypted: crypted, + Plain: plain, + Expiry: config.Expiry, + }, nil +} - code.Expiry = config.Expiry - return code, nil +func verifyCryptoCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, creation time.Time, expiry time.Duration, crypted *crypto.CryptoValue, plain string) error { + gen, _, err := secretGenerator(ctx, filter, typ, alg) + if err != nil { + return err + } + return crypto.VerifyCode(creation, expiry, crypted, plain, gen) } func newCryptoCodeWithPlain(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (value *crypto.CryptoValue, plain string, err error) { - config, err := secretGeneratorConfig(ctx, filter, typ) + gen, _, err := secretGenerator(ctx, filter, typ, alg) if err != nil { return nil, "", err } + return crypto.NewCode(gen) +} +func secretGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (crypto.Generator, *crypto.GeneratorConfig, error) { + config, err := secretGeneratorConfig(ctx, filter, typ) + if err != nil { + return nil, nil, err + } switch a := alg.(type) { case crypto.HashAlgorithm: - return crypto.NewCode(crypto.NewHashGenerator(*config, a)) + return crypto.NewHashGenerator(*config, a), config, nil case crypto.EncryptionAlgorithm: - return crypto.NewCode(crypto.NewEncryptionGenerator(*config, a)) + return crypto.NewEncryptionGenerator(*config, a), config, nil + default: + return nil, nil, errors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", a) } - - return nil, "", errors.ThrowInvalidArgument(nil, "V2-NGESt", "Errors.Internal") } func secretGeneratorConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType) (*crypto.GeneratorConfig, error) { diff --git a/internal/command/crypto_test.go b/internal/command/crypto_test.go new file mode 100644 index 000000000..a846ae43a --- /dev/null +++ b/internal/command/crypto_test.go @@ -0,0 +1,242 @@ +package command + +import ( + "context" + "io" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/internal/command/preparation" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/instance" +) + +func mockCode(code string, exp time.Duration) cryptoCodeFunc { + return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCodeWithExpiry, error) { + return &CryptoCodeWithExpiry{ + Crypted: &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte(code), + }, + Plain: code, + Expiry: exp, + }, nil + } +} + +var ( + testGeneratorConfig = crypto.GeneratorConfig{ + Length: 12, + Expiry: 60000000000, + IncludeLowerLetters: true, + IncludeUpperLetters: true, + IncludeDigits: true, + IncludeSymbols: true, + } +) + +func testSecretGeneratorAddedEvent(typ domain.SecretGeneratorType) *instance.SecretGeneratorAddedEvent { + return instance.NewSecretGeneratorAddedEvent(context.Background(), + &instance.NewAggregate("inst1").Aggregate, typ, + testGeneratorConfig.Length, + testGeneratorConfig.Expiry, + testGeneratorConfig.IncludeLowerLetters, + testGeneratorConfig.IncludeUpperLetters, + testGeneratorConfig.IncludeDigits, + testGeneratorConfig.IncludeSymbols, + ) +} + +func Test_newCryptoCode(t *testing.T) { + type args struct { + typ domain.SecretGeneratorType + alg crypto.Crypto + } + tests := []struct { + name string + eventstore *eventstore.Eventstore + args args + wantErr error + }{ + { + name: "filter config error", + eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + eventstore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newCryptoCodeWithExpiry(context.Background(), tt.eventstore.Filter, tt.args.typ, tt.args.alg) + require.ErrorIs(t, err, tt.wantErr) + if tt.wantErr == nil { + require.NotNil(t, got) + assert.NotNil(t, got.Crypted) + assert.NotEmpty(t, got) + assert.Equal(t, testGeneratorConfig.Expiry, got.Expiry) + } + }) + } +} + +func Test_verifyCryptoCode(t *testing.T) { + es := eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )) + code, err := newCryptoCodeWithExpiry(context.Background(), es.Filter, domain.SecretGeneratorTypeVerifyEmailCode, crypto.CreateMockHashAlg(gomock.NewController(t))) + require.NoError(t, err) + + type args struct { + typ domain.SecretGeneratorType + alg crypto.Crypto + expiry time.Duration + crypted *crypto.CryptoValue + plain string + } + tests := []struct { + name string + eventsore *eventstore.Eventstore + args args + wantErr bool + }{ + { + name: "filter config error", + eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + expiry: code.Expiry, + crypted: code.Crypted, + plain: code.Plain, + }, + wantErr: true, + }, + { + name: "success", + eventsore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + expiry: code.Expiry, + crypted: code.Crypted, + plain: code.Plain, + }, + }, + { + name: "wrong plain", + eventsore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + expiry: code.Expiry, + crypted: code.Crypted, + plain: "wrong", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := verifyCryptoCode(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, time.Now(), tt.args.expiry, tt.args.crypted, tt.args.plain) + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func Test_secretGenerator(t *testing.T) { + type args struct { + typ domain.SecretGeneratorType + alg crypto.Crypto + } + tests := []struct { + name string + eventsore *eventstore.Eventstore + args args + want crypto.Generator + wantConf *crypto.GeneratorConfig + wantErr error + }{ + { + name: "filter config error", + eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "hash generator", + eventsore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockHashAlg(gomock.NewController(t)), + }, + want: crypto.NewHashGenerator(testGeneratorConfig, crypto.CreateMockHashAlg(gomock.NewController(t))), + wantConf: &testGeneratorConfig, + }, + { + name: "encryption generator", + eventsore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + want: crypto.NewEncryptionGenerator(testGeneratorConfig, crypto.CreateMockEncryptionAlg(gomock.NewController(t))), + wantConf: &testGeneratorConfig, + }, + { + name: "unsupported type", + eventsore: eventstoreExpect(t, expectFilter( + eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)), + )), + args: args{ + typ: domain.SecretGeneratorTypeVerifyEmailCode, + alg: nil, + }, + wantErr: errors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, gotConf, err := secretGenerator(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg) + require.ErrorIs(t, err, tt.wantErr) + assert.IsType(t, tt.want, got) + assert.Equal(t, tt.wantConf, gotConf) + }) + } +} diff --git a/internal/command/email.go b/internal/command/email.go index 0bfbcd6af..f54b3de8e 100644 --- a/internal/command/email.go +++ b/internal/command/email.go @@ -23,6 +23,6 @@ func (e *Email) Validate() error { return e.Address.Validate() } -func newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { - return newCryptoCodeWithExpiry(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg) +func (c *Commands) newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { + return c.newCode(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg) } diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go index bfbe48ad5..5d6f131c2 100644 --- a/internal/command/user_human_test.go +++ b/internal/command/user_human_test.go @@ -31,7 +31,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator id.Generator userPasswordAlg crypto.HashAlgorithm codeAlg crypto.EncryptionAlgorithm - newEmailCode func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) + newCode cryptoCodeFunc } type args struct { ctx context.Context @@ -446,7 +446,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - newEmailCode: mockEmailCode("emailCode", time.Hour), + newCode: mockCode("emailCode", time.Hour), }, args: args{ ctx: context.Background(), @@ -526,7 +526,7 @@ func TestCommandSide_AddHuman(t *testing.T) { idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)), codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - newEmailCode: mockEmailCode("emailCode", time.Hour), + newCode: mockCode("emailCode", time.Hour), }, args: args{ ctx: context.Background(), @@ -1202,7 +1202,7 @@ func TestCommandSide_AddHuman(t *testing.T) { userPasswordAlg: tt.fields.userPasswordAlg, userEncryption: tt.fields.codeAlg, idGenerator: tt.fields.idGenerator, - newEmailCode: tt.fields.newEmailCode, + newCode: tt.fields.newCode, } err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail) if tt.res.err == nil { @@ -3992,18 +3992,3 @@ func TestAddHumanCommand(t *testing.T) { }) } } - -func mockEmailCode(code string, exp time.Duration) func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCodeWithExpiry, error) { - return &CryptoCodeWithExpiry{ - Crypted: &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte(code), - }, - Plain: code, - Expiry: exp, - }, nil - } -} diff --git a/internal/command/user_human_webauthn.go b/internal/command/user_human_webauthn.go index 2d9d8db7a..5156b5f8f 100644 --- a/internal/command/user_human_webauthn.go +++ b/internal/command/user_human_webauthn.go @@ -539,7 +539,7 @@ func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, res } if !direct { codeEventCreator = func(ctx context.Context, agg *eventstore.Aggregate, id string, cryptoCode *crypto.CryptoValue, exp time.Duration) eventstore.Command { - return usr_repo.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, id, cryptoCode, exp) + return usr_repo.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, id, cryptoCode, exp, "", false) } } codeEvent := codeEventCreator(ctx, UserAggregateFromWriteModel(&initCode.WriteModel), codeID, cryptoCode, passwordlessCodeGenerator.Expiry()) diff --git a/internal/command/user_human_webauthn_model.go b/internal/command/user_human_webauthn_model.go index bbf61ee95..b92e35dbd 100644 --- a/internal/command/user_human_webauthn_model.go +++ b/internal/command/user_human_webauthn_model.go @@ -512,6 +512,9 @@ func (wm *HumanPasswordlessInitCodeWriteModel) appendRequestedEvent(e *user.Huma wm.CryptoCode = e.Code wm.Expiration = e.Expiry wm.State = domain.PasswordlessInitCodeStateRequested + if e.CodeReturned { + wm.State = domain.PasswordlessInitCodeStateActive + } } func (wm *HumanPasswordlessInitCodeWriteModel) appendCheckFailedEvent(e *user.HumanPasswordlessInitCodeCheckFailedEvent) { diff --git a/internal/command/user_v2_email_test.go b/internal/command/user_v2_email_test.go index af6478473..dfcbe9107 100644 --- a/internal/command/user_v2_email_test.go +++ b/internal/command/user_v2_email_test.go @@ -199,7 +199,7 @@ func TestCommands_ChangeUserEmailURLTemplate(t *testing.T) { email: "email-changed@test.ch", urlTmpl: "{{", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), }, { name: "permission missing", diff --git a/internal/command/user_v2_passkey.go b/internal/command/user_v2_passkey.go new file mode 100644 index 000000000..2795e6df8 --- /dev/null +++ b/internal/command/user_v2_passkey.go @@ -0,0 +1,156 @@ +package command + +import ( + "context" + "io" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/user" +) + +// RegisterUserPasskey creates a passkey registration for the current authenticated user. +// UserID, ussualy taken from the request is compaired against the user ID in the context. +func (c *Commands) RegisterUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment) (*domain.PasskeyRegistrationDetails, error) { + if err := authz.UserIDInCTX(ctx, userID); err != nil { + return nil, err + } + return c.registerUserPasskey(ctx, userID, resourceOwner, authenticator) +} + +// RegisterUserPasskeyWithCode registers a new passkey for a unauthenticated user id. +// The resource is protected by the code, identified by the codeID. +func (c *Commands) RegisterUserPasskeyWithCode(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment, codeID, code string, alg crypto.EncryptionAlgorithm) (*domain.PasskeyRegistrationDetails, error) { + event, err := c.verifyUserPasskeyCode(ctx, userID, resourceOwner, codeID, code, alg) + if err != nil { + return nil, err + } + + return c.registerUserPasskey(ctx, userID, resourceOwner, authenticator, event) +} + +type eventCallback func(context.Context, *eventstore.Aggregate) eventstore.Command + +// verifyUserPasskeyCode verifies a passkey code, identified by codeID and userID. +// A code can only be used once. +// Upon success an event callback is returned, which must be called after +// all other events for the current request are created. +// This prevent consuming a code when another error occurred after verification. +func (c *Commands) verifyUserPasskeyCode(ctx context.Context, userID, resourceOwner, codeID, code string, alg crypto.EncryptionAlgorithm) (eventCallback, error) { + wm := NewHumanPasswordlessInitCodeWriteModel(userID, codeID, resourceOwner) + err := c.eventstore.FilterToQueryReducer(ctx, wm) + if err != nil { + return nil, err + } + err = verifyCryptoCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg, wm.ChangeDate, wm.Expiration, wm.CryptoCode, code) + if err != nil || wm.State != domain.PasswordlessInitCodeStateActive { + c.verifyUserPasskeyCodeFailed(ctx, wm) + return nil, caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid") + } + return func(ctx context.Context, userAgg *eventstore.Aggregate) eventstore.Command { + return user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, codeID) + }, nil +} + +func (c *Commands) verifyUserPasskeyCodeFailed(ctx context.Context, wm *HumanPasswordlessInitCodeWriteModel) { + userAgg := UserAggregateFromWriteModel(&wm.WriteModel) + _, err := c.eventstore.Push(ctx, user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, wm.CodeID)) + logging.WithFields("userID", userAgg.ID).OnError(err).Error("RegisterUserPasskeyWithCode push failed") +} + +func (c *Commands) registerUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment, events ...eventCallback) (*domain.PasskeyRegistrationDetails, error) { + wm, userAgg, webAuthN, err := c.createUserPasskey(ctx, userID, resourceOwner, authenticator) + if err != nil { + return nil, err + } + return c.pushUserPasskey(ctx, wm, userAgg, webAuthN, events...) +} + +func (c *Commands) createUserPasskey(ctx context.Context, userID, resourceOwner string, authenticator domain.AuthenticatorAttachment) (*HumanWebAuthNWriteModel, *eventstore.Aggregate, *domain.WebAuthNToken, error) { + passwordlessTokens, err := c.getHumanPasswordlessTokens(ctx, userID, resourceOwner) + if err != nil { + return nil, nil, nil, err + } + return c.addHumanWebAuthN(ctx, userID, resourceOwner, false, passwordlessTokens, authenticator, domain.UserVerificationRequirementRequired) +} + +func (c *Commands) pushUserPasskey(ctx context.Context, wm *HumanWebAuthNWriteModel, userAgg *eventstore.Aggregate, webAuthN *domain.WebAuthNToken, events ...eventCallback) (*domain.PasskeyRegistrationDetails, error) { + cmds := make([]eventstore.Command, len(events)+1) + cmds[0] = user.NewHumanPasswordlessAddedEvent(ctx, userAgg, wm.WebauthNTokenID, webAuthN.Challenge) + for i, event := range events { + cmds[i+1] = event(ctx, userAgg) + } + + err := c.pushAppendAndReduce(ctx, wm, cmds...) + if err != nil { + return nil, err + } + return &domain.PasskeyRegistrationDetails{ + ObjectDetails: writeModelToObjectDetails(&wm.WriteModel), + PasskeyID: wm.WebauthNTokenID, + PublicKeyCredentialCreationOptions: webAuthN.CredentialCreationData, + }, nil +} + +// AddUserPasskeyCode generates a Passkey code and sends an email +// with the default generated URL (pointing to zitadel). +func (c *Commands) AddUserPasskeyCode(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm) (*domain.ObjectDetails, error) { + details, err := c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, "", false) + if err != nil { + return nil, err + } + return details.ObjectDetails, err +} + +// AddUserPasskeyCodeURLTemplate generates a Passkey code and sends an email +// with the URL created from passed template string. +// The template is executed as a test, before pushing to the eventstore. +func (c *Commands) AddUserPasskeyCodeURLTemplate(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm, urlTmpl string) (*domain.ObjectDetails, error) { + if err := domain.RenderPasskeyURLTemplate(io.Discard, urlTmpl, userID, resourceOwner, "codeID", "code"); err != nil { + return nil, err + } + details, err := c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, urlTmpl, false) + if err != nil { + return nil, err + } + return details.ObjectDetails, err +} + +// AddUserPasskeyCodeReturn generates and returns a Passkey code. +// No email will be send to the user. +func (c *Commands) AddUserPasskeyCodeReturn(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm) (*domain.PasskeyCodeDetails, error) { + return c.addUserPasskeyCode(ctx, userID, resourceOwner, alg, "", true) +} + +func (c *Commands) addUserPasskeyCode(ctx context.Context, userID, resourceOwner string, alg crypto.EncryptionAlgorithm, urlTmpl string, returnCode bool) (*domain.PasskeyCodeDetails, error) { + codeID, err := c.idGenerator.Next() + if err != nil { + return nil, err + } + code, err := c.newCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) + if err != nil { + return nil, err + } + wm := NewHumanPasswordlessInitCodeWriteModel(userID, codeID, resourceOwner) + err = c.eventstore.FilterToQueryReducer(ctx, wm) + if err != nil { + return nil, err + } + agg := UserAggregateFromWriteModel(&wm.WriteModel) + + cmd := user.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, codeID, code.Crypted, code.Expiry, urlTmpl, returnCode) + err = c.pushAppendAndReduce(ctx, wm, cmd) + if err != nil { + return nil, err + } + return &domain.PasskeyCodeDetails{ + ObjectDetails: writeModelToObjectDetails(&wm.WriteModel), + CodeID: codeID, + Code: code.Plain, + }, nil +} diff --git a/internal/command/user_v2_passkey_test.go b/internal/command/user_v2_passkey_test.go new file mode 100644 index 000000000..aefa9685a --- /dev/null +++ b/internal/command/user_v2_passkey_test.go @@ -0,0 +1,915 @@ +package command + +import ( + "context" + "io" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/id" + id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/org" + "github.com/zitadel/zitadel/internal/repository/user" + webauthn_helper "github.com/zitadel/zitadel/internal/webauthn" +) + +func TestCommands_RegisterUserPasskey(t *testing.T) { + ctx := authz.NewMockContextWithPermissions("instance1", "org1", "user1", nil) + ctx = authz.WithRequestedDomain(ctx, "example.com") + + webauthnConfig := &webauthn_helper.Config{ + DisplayName: "test", + ExternalSecure: true, + } + userAgg := &user.NewAggregate("user1", "org1").Aggregate + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + authenticator domain.AuthenticatorAttachment + } + tests := []struct { + name string + fields fields + args args + want *domain.PasskeyRegistrationDetails + wantErr error + }{ + { + name: "wrong user", + args: args{ + userID: "foo", + resourceOwner: "org1", + authenticator: domain.AuthenticatorAttachmentCrossPlattform, + }, + wantErr: caos_errs.ThrowUnauthenticated(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong"), + }, + { + name: "get human passwordless error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilterError(io.ErrClosedPipe), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + authenticator: domain.AuthenticatorAttachmentCrossPlattform, + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "id generator error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), // getHumanPasswordlessTokens + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(ctx, + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectFilter(eventFromEventPusher( + org.NewOrgAddedEvent(ctx, + &org.NewAggregate("org1").Aggregate, + "org1", + ), + )), + expectFilter(eventFromEventPusher( + org.NewDomainPolicyAddedEvent(ctx, + &org.NewAggregate("org1").Aggregate, + false, false, false, + ), + )), + ), + idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + authenticator: domain.AuthenticatorAttachmentCrossPlattform, + }, + wantErr: io.ErrClosedPipe, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + webauthnConfig: webauthnConfig, + } + _, err := c.RegisterUserPasskey(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authenticator) + require.ErrorIs(t, err, tt.wantErr) + // successful case can't be tested due to random challenge. + }) + } +} + +func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) { + ctx := authz.WithRequestedDomain(context.Background(), "example.com") + webauthnConfig := &webauthn_helper.Config{ + DisplayName: "test", + ExternalSecure: true, + } + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + es := eventstoreExpect(t, + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + ) + code, err := newCryptoCodeWithExpiry(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) + require.NoError(t, err) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + authenticator domain.AuthenticatorAttachment + codeID string + code string + } + tests := []struct { + name string + fields fields + args args + wantErr error + }{ + { + name: "code verification error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, "123", code.Crypted, time.Minute, "", false, + ), + ), + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), + ), + ), + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + expectPush([]*repository.Event{eventFromEventPusher( + user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), + )}), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + authenticator: domain.AuthenticatorAttachmentCrossPlattform, + codeID: "123", + code: "wrong", + }, + wantErr: caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid"), + }, + { + name: "code verification ok, get human passwordless error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, "123", code.Crypted, time.Minute, "", false, + ), + ), + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), + ), + ), + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + expectFilterError(io.ErrClosedPipe), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + authenticator: domain.AuthenticatorAttachmentCrossPlattform, + codeID: "123", + code: code.Plain, + }, + wantErr: io.ErrClosedPipe, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + webauthnConfig: webauthnConfig, + } + _, err := c.RegisterUserPasskeyWithCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authenticator, tt.args.codeID, tt.args.code, alg) + require.ErrorIs(t, err, tt.wantErr) + // successful case can't be tested due to random challenge. + }) + } +} + +func TestCommands_verifyUserPasskeyCode(t *testing.T) { + ctx := authz.WithRequestedDomain(context.Background(), "example.com") + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + es := eventstoreExpect(t, + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + ) + code, err := newCryptoCodeWithExpiry(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) + require.NoError(t, err) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + + type fields struct { + eventstore *eventstore.Eventstore + } + type args struct { + userID string + resourceOwner string + codeID string + code string + } + tests := []struct { + name string + fields fields + args args + want *user.HumanPasswordlessInitCodeCheckSucceededEvent + wantErr error + }{ + { + name: "filter error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilterError(io.ErrClosedPipe), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + codeID: "123", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "code verification error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, "123", code.Crypted, time.Minute, "", false, + ), + ), + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), + ), + ), + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + expectPush([]*repository.Event{eventFromEventPusher( + user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), + )}), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + codeID: "123", + code: "wrong", + }, + wantErr: caos_errs.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid"), + }, + { + name: "success", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, "123", code.Crypted, time.Minute, "", false, + ), + ), + eventFromEventPusherWithCreationDateNow( + user.NewHumanPasswordlessInitCodeSentEvent(ctx, userAgg, "123"), + ), + ), + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + ), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + codeID: "123", + code: code.Plain, + }, + want: user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + } + got, err := c.verifyUserPasskeyCode(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.codeID, tt.args.code, alg) + require.ErrorIs(t, err, tt.wantErr) + if tt.wantErr == nil { + assert.Equal(t, tt.want, got(ctx, userAgg)) + } + }) + } +} + +func TestCommands_pushUserPasskey(t *testing.T) { + ctx := authz.WithRequestedDomain(context.Background(), "example.com") + webauthnConfig := &webauthn_helper.Config{ + DisplayName: "test", + ExternalSecure: true, + } + userAgg := &user.NewAggregate("user1", "org1").Aggregate + + prep := []expect{ + expectFilter(), // getHumanPasswordlessTokens + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(ctx, + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectFilter(eventFromEventPusher( + org.NewOrgAddedEvent(ctx, + &org.NewAggregate("org1").Aggregate, + "org1", + ), + )), + expectFilter(eventFromEventPusher( + org.NewDomainPolicyAddedEvent(ctx, + &org.NewAggregate("org1").Aggregate, + false, false, false, + ), + )), + expectFilter(eventFromEventPusher( + user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush( + ctx, &org.NewAggregate("org1").Aggregate, user.HumanPasswordlessTokenAddedType, + ), "111", "challenge"), + )), + } + + type args struct { + events []eventCallback + } + tests := []struct { + name string + expectPush func(challenge string) expect + args args + wantErr error + }{ + { + name: "push error", + expectPush: func(challenge string) expect { + return expectPushFailed(io.ErrClosedPipe, []*repository.Event{eventFromEventPusher( + user.NewHumanPasswordlessAddedEvent(ctx, + userAgg, "123", challenge, + ), + )}) + }, + args: args{}, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + expectPush: func(challenge string) expect { + return expectPush([]*repository.Event{eventFromEventPusher( + user.NewHumanPasswordlessAddedEvent(ctx, + userAgg, "123", challenge, + ), + )}) + }, + args: args{}, + }, + { + name: "initcode succeeded event", + expectPush: func(challenge string) expect { + return expectPush([]*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessAddedEvent(ctx, + userAgg, "123", challenge, + ), + ), + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), + ), + }) + }, + args: args{ + events: []eventCallback{func(ctx context.Context, userAgg *eventstore.Aggregate) eventstore.Command { + return user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123") + }}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: eventstoreExpect(t, prep...), + webauthnConfig: webauthnConfig, + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + } + wm, userAgg, webAuthN, err := c.createUserPasskey(ctx, "user1", "org1", domain.AuthenticatorAttachmentCrossPlattform) + require.NoError(t, err) + + c.eventstore = eventstoreExpect(t, tt.expectPush(webAuthN.Challenge)) + + got, err := c.pushUserPasskey(ctx, wm, userAgg, webAuthN, tt.args.events...) + require.ErrorIs(t, err, tt.wantErr) + if tt.wantErr == nil { + assert.NotEmpty(t, got.PublicKeyCredentialCreationOptions) + assert.Equal(t, "123", got.PasskeyID) + assert.Equal(t, "org1", got.ObjectDetails.ResourceOwner) + } + }) + } +} + +func TestCommands_AddUserPasskeyCode(t *testing.T) { + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + type fields struct { + newCode cryptoCodeFunc + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + } + tests := []struct { + name string + fields fields + args args + want *domain.ObjectDetails + wantErr error + }{ + { + name: "id generator error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + fields: fields{ + newCode: mockCode("passkey1", time.Minute), + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectPush([]*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, + ), + ), + }), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + newCode: tt.fields.newCode, + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := c.AddUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) { + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + + type fields struct { + newCode cryptoCodeFunc + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + urlTmpl string + } + tests := []struct { + name string + fields fields + args args + want *domain.ObjectDetails + wantErr error + }{ + { + name: "template error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + urlTmpl: "{{", + }, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + }, + { + name: "id generator error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + fields: fields{ + newCode: mockCode("passkey1", time.Minute), + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectPush([]*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, + time.Minute, + "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + false, + ), + ), + }), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + }, + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + newCode: tt.fields.newCode, + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := c.AddUserPasskeyCodeURLTemplate(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, tt.args.urlTmpl) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) { + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + type fields struct { + newCode cryptoCodeFunc + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + } + tests := []struct { + name string + fields fields + args args + want *domain.PasskeyCodeDetails + wantErr error + }{ + { + name: "id generator error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + fields: fields{ + newCode: mockCode("passkey1", time.Minute), + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectPush([]*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", true, + ), + ), + }), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + want: &domain.PasskeyCodeDetails{ + ObjectDetails: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + CodeID: "123", + Code: "passkey1", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + newCode: tt.fields.newCode, + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := c.AddUserPasskeyCodeReturn(context.Background(), tt.args.userID, tt.args.resourceOwner, alg) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestCommands_addUserPasskeyCode(t *testing.T) { + alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t)) + userAgg := &user.NewAggregate("user1", "org1").Aggregate + type fields struct { + newCode cryptoCodeFunc + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + userID string + resourceOwner string + } + tests := []struct { + name string + fields fields + args args + want *domain.PasskeyCodeDetails + wantErr error + }{ + { + name: "id generator error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t), + idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "crypto error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "filter query error", + fields: fields{ + newCode: newCryptoCodeWithExpiry, + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), + expectFilterError(io.ErrClosedPipe), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "push error", + fields: fields{ + newCode: mockCode("passkey1", time.Minute), + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectPushFailed(io.ErrClosedPipe, []*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, + ), + ), + }), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + wantErr: io.ErrClosedPipe, + }, + { + name: "success", + fields: fields{ + newCode: mockCode("passkey1", time.Minute), + eventstore: eventstoreExpect(t, + expectFilter(eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAgg, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + )), + expectPush([]*repository.Event{ + eventFromEventPusher( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, + ), + ), + }), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), + }, + args: args{ + userID: "user1", + resourceOwner: "org1", + }, + want: &domain.PasskeyCodeDetails{ + ObjectDetails: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + CodeID: "123", + Code: "passkey1", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + newCode: tt.fields.newCode, + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := c.addUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, "", false) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/domain/human_email.go b/internal/domain/human_email.go index 578c9fe22..86fd2f9e1 100644 --- a/internal/domain/human_email.go +++ b/internal/domain/human_email.go @@ -4,12 +4,10 @@ import ( "io" "regexp" "strings" - "text/template" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" - caos_errs "github.com/zitadel/zitadel/internal/errors" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" ) @@ -73,19 +71,8 @@ type ConfirmURLData struct { OrgID string } -// RenderConfirmURLTemplate parses and renders tmplStr. +// RenderConfirmURLTemplate parses and renders tmpl. // userID, code and orgID are passed into the [ConfirmURLData]. -// "%s%s?userID=%s&code=%s&orgID=%s" -func RenderConfirmURLTemplate(w io.Writer, tmplStr, userID, code, orgID string) error { - tmpl, err := template.New("").Parse(tmplStr) - if err != nil { - return caos_errs.ThrowInvalidArgument(err, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate") - } - - data := &ConfirmURLData{userID, code, orgID} - if err = tmpl.Execute(w, data); err != nil { - return caos_errs.ThrowInvalidArgument(err, "USERv2-ohSi5", "Errors.User.Email.InvalidURLTemplate") - } - - return nil +func RenderConfirmURLTemplate(w io.Writer, tmpl, userID, code, orgID string) error { + return renderURLTemplate(w, tmpl, &ConfirmURLData{userID, code, orgID}) } diff --git a/internal/domain/human_email_test.go b/internal/domain/human_email_test.go index f77ba6d5c..4b30dd667 100644 --- a/internal/domain/human_email_test.go +++ b/internal/domain/human_email_test.go @@ -81,10 +81,10 @@ func TestEmailValid(t *testing.T) { func TestRenderConfirmURLTemplate(t *testing.T) { type args struct { - tmplStr string - userID string - code string - orgID string + tmpl string + userID string + code string + orgID string } tests := []struct { name string @@ -95,30 +95,30 @@ func TestRenderConfirmURLTemplate(t *testing.T) { { name: "invalid template", args: args{ - tmplStr: "{{", - userID: "user1", - code: "123", - orgID: "org1", + tmpl: "{{", + userID: "user1", + code: "123", + orgID: "org1", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), }, { name: "execution error", args: args{ - tmplStr: "{{.Foo}}", - userID: "user1", - code: "123", - orgID: "org1", + tmpl: "{{.Foo}}", + userID: "user1", + code: "123", + orgID: "org1", }, - wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ohSi5", "Errors.User.Email.InvalidURLTemplate"), + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate"), }, { name: "success", args: args{ - tmplStr: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", - userID: "user1", - code: "123", - orgID: "org1", + tmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + userID: "user1", + code: "123", + orgID: "org1", }, want: "https://example.com/email/verify?userID=user1&code=123&orgID=org1", }, @@ -126,7 +126,7 @@ func TestRenderConfirmURLTemplate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var w strings.Builder - err := RenderConfirmURLTemplate(&w, tt.args.tmplStr, tt.args.userID, tt.args.code, tt.args.orgID) + err := RenderConfirmURLTemplate(&w, tt.args.tmpl, tt.args.userID, tt.args.code, tt.args.orgID) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.want, w.String()) }) diff --git a/internal/domain/url_template.go b/internal/domain/url_template.go new file mode 100644 index 000000000..172de3496 --- /dev/null +++ b/internal/domain/url_template.go @@ -0,0 +1,19 @@ +package domain + +import ( + "io" + "text/template" + + caos_errs "github.com/zitadel/zitadel/internal/errors" +) + +func renderURLTemplate(w io.Writer, tmpl string, data any) error { + parsed, err := template.New("").Parse(tmpl) + if err != nil { + return caos_errs.ThrowInvalidArgument(err, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate") + } + if err = parsed.Execute(w, data); err != nil { + return caos_errs.ThrowInvalidArgument(err, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate") + } + return nil +} diff --git a/internal/domain/url_template_test.go b/internal/domain/url_template_test.go new file mode 100644 index 000000000..1140d1424 --- /dev/null +++ b/internal/domain/url_template_test.go @@ -0,0 +1,56 @@ +package domain + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + caos_errs "github.com/zitadel/zitadel/internal/errors" +) + +func Test_renderURLTemplate(t *testing.T) { + type args struct { + tmpl string + data any + } + tests := []struct { + name string + args args + wantW string + wantErr error + }{ + { + name: "parse error", + args: args{ + tmpl: "{{", + }, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + }, + { + name: "execution error", + args: args{ + tmpl: "{{.Some}}", + data: struct{ Foo int }{Foo: 1}, + }, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-ieYa7", "Errors.User.InvalidURLTemplate"), + }, + { + name: "success", + args: args{ + tmpl: "{{.Foo}}", + data: struct{ Foo int }{Foo: 1}, + }, + wantW: "1", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := renderURLTemplate(w, tt.args.tmpl, tt.args.data) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.wantW, w.String()) + }) + } +} diff --git a/internal/domain/user_v2_passkey.go b/internal/domain/user_v2_passkey.go new file mode 100644 index 000000000..37f34097e --- /dev/null +++ b/internal/domain/user_v2_passkey.go @@ -0,0 +1,29 @@ +package domain + +import "io" + +type PasskeyURLData struct { + UserID string + OrgID string + CodeID string + Code string +} + +// RenderPasskeyURLTemplate parses and renders tmpl. +// userID, orgID, codeID and code are passed into the [PasskeyURLData]. +func RenderPasskeyURLTemplate(w io.Writer, tmpl, userID, orgID, codeID, code string) error { + return renderURLTemplate(w, tmpl, &PasskeyURLData{userID, orgID, codeID, code}) +} + +type PasskeyCodeDetails struct { + *ObjectDetails + CodeID string + Code string +} + +type PasskeyRegistrationDetails struct { + *ObjectDetails + + PasskeyID string + PublicKeyCredentialCreationOptions []byte +} diff --git a/internal/domain/user_v2_passkey_test.go b/internal/domain/user_v2_passkey_test.go new file mode 100644 index 000000000..74cb1695e --- /dev/null +++ b/internal/domain/user_v2_passkey_test.go @@ -0,0 +1,54 @@ +package domain + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + caos_errs "github.com/zitadel/zitadel/internal/errors" +) + +func TestRenderPasskeyURLTemplate(t *testing.T) { + type args struct { + tmpl string + userID string + orgID string + codeID string + code string + } + tests := []struct { + name string + args args + wantW string + wantErr error + }{ + { + name: "parse error", + args: args{ + tmpl: "{{", + }, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + }, + { + name: "success", + args: args{ + tmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + userID: "user1", + orgID: "org1", + codeID: "99", + code: "123", + }, + wantW: "https://example.com/passkey/register?userID=user1&orgID=org1&codeID=99&code=123", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := RenderPasskeyURLTemplate(w, tt.args.tmpl, tt.args.userID, tt.args.orgID, tt.args.codeID, tt.args.code) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.wantW, w.String()) + }) + } +} diff --git a/internal/notification/handlers/usernotifier.go b/internal/notification/handlers/usernotifier.go index 10b0952ce..5cd810236 100644 --- a/internal/notification/handlers/usernotifier.go +++ b/internal/notification/handlers/usernotifier.go @@ -394,6 +394,9 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-EDtjd", "reduce.wrong.event.type %s", user.HumanPasswordlessInitCodeAddedType) } + if e.CodeReturned { + return crdb.NewNoOpStatement(e), nil + } ctx := HandlerContext(event.Aggregate()) alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType) if err != nil { @@ -442,7 +445,7 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) ( e, u.metricSuccessfulDeliveriesEmail, u.metricFailedDeliveriesEmail, - ).SendPasswordlessRegistrationLink(notifyUser, origin, code, e.ID) + ).SendPasswordlessRegistrationLink(notifyUser, origin, code, e.ID, e.URLTemplate) if err != nil { return nil, err } diff --git a/internal/notification/types/email_verification_code_test.go b/internal/notification/types/email_verification_code_test.go index 0e1975fbc..71cebaca5 100644 --- a/internal/notification/types/email_verification_code_test.go +++ b/internal/notification/types/email_verification_code_test.go @@ -12,27 +12,6 @@ import ( ) func TestNotify_SendEmailVerificationCode(t *testing.T) { - type res struct { - url string - args map[string]interface{} - messageType string - allowUnverifiedNotificationChannel bool - } - notify := func(dst *res) Notify { - return func( - url string, - args map[string]interface{}, - messageType string, - allowUnverifiedNotificationChannel bool, - ) error { - dst.url = url - dst.args = args - dst.messageType = messageType - dst.allowUnverifiedNotificationChannel = allowUnverifiedNotificationChannel - return nil - } - } - type args struct { user *query.NotifyUser origin string @@ -42,7 +21,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { tests := []struct { name string args args - want *res + want *notifyResult wantErr error }{ { @@ -56,7 +35,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "", }, - want: &res{ + want: ¬ifyResult{ url: "https://example.com/ui/login/mail/verification?userID=user1&code=123&orgID=org1", args: map[string]interface{}{"Code": "123"}, messageType: domain.VerifyEmailMessageType, @@ -74,8 +53,8 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "{{", }, - want: &res{}, - wantErr: caos_errs.ThrowInvalidArgument(nil, "USERv2-ooD8p", "Errors.User.Email.InvalidURLTemplate"), + want: ¬ifyResult{}, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), }, { name: "template success", @@ -88,7 +67,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { code: "123", urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", }, - want: &res{ + want: ¬ifyResult{ url: "https://example.com/email/verify?userID=user1&code=123&orgID=org1", args: map[string]interface{}{"Code": "123"}, messageType: domain.VerifyEmailMessageType, @@ -98,8 +77,8 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := new(res) - err := notify(got).SendEmailVerificationCode(tt.args.user, tt.args.origin, tt.args.code, tt.args.urlTmpl) + got, notify := mockNotify() + err := notify.SendEmailVerificationCode(tt.args.user, tt.args.origin, tt.args.code, tt.args.urlTmpl) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.want, got) }) diff --git a/internal/notification/types/passwordless_registration_link.go b/internal/notification/types/passwordless_registration_link.go index 8c6f6894b..7f4ab0040 100644 --- a/internal/notification/types/passwordless_registration_link.go +++ b/internal/notification/types/passwordless_registration_link.go @@ -1,12 +1,24 @@ package types import ( + "strings" + "github.com/zitadel/zitadel/internal/api/ui/login" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" ) -func (notify Notify) SendPasswordlessRegistrationLink(user *query.NotifyUser, origin, code, codeID string) error { - url := domain.PasswordlessInitCodeLink(origin+login.HandlerPrefix+login.EndpointPasswordlessRegistration, user.ID, user.ResourceOwner, codeID, code) +func (notify Notify) SendPasswordlessRegistrationLink(user *query.NotifyUser, origin, code, codeID, urlTmpl string) error { + var url string + if urlTmpl == "" { + url = domain.PasswordlessInitCodeLink(origin+login.HandlerPrefix+login.EndpointPasswordlessRegistration, user.ID, user.ResourceOwner, codeID, code) + } else { + var buf strings.Builder + if err := domain.RenderPasskeyURLTemplate(&buf, urlTmpl, user.ID, user.ResourceOwner, codeID, code); err != nil { + return err + } + url = buf.String() + } + return notify(url, nil, domain.PasswordlessRegistrationMessageType, true) } diff --git a/internal/notification/types/passwordless_registration_link_test.go b/internal/notification/types/passwordless_registration_link_test.go new file mode 100644 index 000000000..eb13b94a4 --- /dev/null +++ b/internal/notification/types/passwordless_registration_link_test.go @@ -0,0 +1,88 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/query" +) + +func TestNotify_SendPasswordlessRegistrationLink(t *testing.T) { + type args struct { + user *query.NotifyUser + origin string + code string + codeID string + urlTmpl string + } + tests := []struct { + name string + args args + want *notifyResult + wantErr error + }{ + { + name: "default URL", + args: args{ + user: &query.NotifyUser{ + ID: "user1", + ResourceOwner: "org1", + }, + origin: "https://example.com", + code: "123", + codeID: "456", + urlTmpl: "", + }, + want: ¬ifyResult{ + url: "https://example.com/ui/login/login/passwordless/init?userID=user1&orgID=org1&codeID=456&code=123", + messageType: domain.PasswordlessRegistrationMessageType, + allowUnverifiedNotificationChannel: true, + }, + }, + { + name: "template error", + args: args{ + user: &query.NotifyUser{ + ID: "user1", + ResourceOwner: "org1", + }, + origin: "https://example.com", + code: "123", + codeID: "456", + urlTmpl: "{{", + }, + want: ¬ifyResult{}, + wantErr: caos_errs.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), + }, + { + name: "template success", + args: args{ + user: &query.NotifyUser{ + ID: "user1", + ResourceOwner: "org1", + }, + origin: "https://example.com", + code: "123", + codeID: "456", + urlTmpl: "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + }, + want: ¬ifyResult{ + url: "https://example.com/passkey/register?userID=user1&orgID=org1&codeID=456&code=123", + messageType: domain.PasswordlessRegistrationMessageType, + allowUnverifiedNotificationChannel: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, notify := mockNotify() + err := notify.SendPasswordlessRegistrationLink(tt.args.user, tt.args.origin, tt.args.code, tt.args.codeID, tt.args.urlTmpl) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/notification/types/types_test.go b/internal/notification/types/types_test.go new file mode 100644 index 000000000..1b5066d19 --- /dev/null +++ b/internal/notification/types/types_test.go @@ -0,0 +1,23 @@ +package types + +type notifyResult struct { + url string + args map[string]interface{} + messageType string + allowUnverifiedNotificationChannel bool +} + +// mockNotify returns a notifyResult and Notify function for easy mocking. +// The notifyResult will only be populated after Notify is called. +func mockNotify() (*notifyResult, Notify) { + dst := new(notifyResult) + return dst, func(url string, args map[string]interface{}, messageType string, allowUnverifiedNotificationChannel bool) error { + *dst = notifyResult{ + url: url, + args: args, + messageType: messageType, + allowUnverifiedNotificationChannel: allowUnverifiedNotificationChannel, + } + return nil + } +} diff --git a/internal/repository/user/human_mfa_passwordless.go b/internal/repository/user/human_mfa_passwordless.go index 13889277f..f5e68b639 100644 --- a/internal/repository/user/human_mfa_passwordless.go +++ b/internal/repository/user/human_mfa_passwordless.go @@ -315,9 +315,11 @@ func HumanPasswordlessInitCodeAddedEventMapper(event *repository.Event) (eventst type HumanPasswordlessInitCodeRequestedEvent struct { eventstore.BaseEvent `json:"-"` - ID string `json:"id"` - Code *crypto.CryptoValue `json:"code"` - Expiry time.Duration `json:"expiry"` + ID string `json:"id"` + Code *crypto.CryptoValue `json:"code"` + Expiry time.Duration `json:"expiry"` + URLTemplate string `json:"url_template,omitempty"` + CodeReturned bool `json:"code_returned,omitempty"` } func (e *HumanPasswordlessInitCodeRequestedEvent) Data() interface{} { @@ -334,6 +336,8 @@ func NewHumanPasswordlessInitCodeRequestedEvent( id string, code *crypto.CryptoValue, expiry time.Duration, + urlTmpl string, + codeReturned bool, ) *HumanPasswordlessInitCodeRequestedEvent { return &HumanPasswordlessInitCodeRequestedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -341,9 +345,11 @@ func NewHumanPasswordlessInitCodeRequestedEvent( aggregate, HumanPasswordlessInitCodeRequestedType, ), - ID: id, - Code: code, - Expiry: expiry, + ID: id, + Code: code, + Expiry: expiry, + URLTemplate: urlTmpl, + CodeReturned: codeReturned, } } diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index a0b5a1f89..441b6be36 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an UserIDMissing: User ID fehlt + UserIDWrong: "Der Anforderungsbenutzer ist nicht gleich dem authentifizierten Benutzer" DomainPolicyNil: Organisation Policy ist leer EmailAsUsernameNotAllowed: Benutzername darf keine E-Mail Adresse sein Invalid: Benutzerdaten sind ungültig @@ -67,6 +68,7 @@ Errors: NoChanges: Keine Änderungen gefunden InitCodeNotFound: Kein Initialisierungs-Code gefunden UsernameNotChanged: Benutzername wurde nicht verändert + InvalidURLTemplate: URL Template ist ungültig Profile: NotFound: Profil nicht gefunden NotChanged: Profil nicht verändert @@ -81,7 +83,6 @@ Errors: NotChanged: Email wurde nicht geändert Empty: Email ist leer IDMissing: Email ID fehlt - InvalidURLTemplate: URL Template ist ungültig Phone: NotFound: Telefonnummer nicht gefunden Invalid: Telefonnummer ist ungültig diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 5dfb9d5aa..276ad07da 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: User could not be found on chosen organization NotAllowedOrg: User is no member of the required organization UserIDMissing: User ID missing + UserIDWrong: "Request user not equal to authenticated user" DomainPolicyNil: Organisation Policy is empty EmailAsUsernameNotAllowed: Email is not allowed as username Invalid: Userdata is invalid @@ -67,6 +68,7 @@ Errors: NoChanges: No changes found InitCodeNotFound: Initialization Code not found UsernameNotChanged: Username not changed + InvalidURLTemplate: URL Template is invalid Profile: NotFound: Profile not found NotChanged: Profile not changed @@ -81,7 +83,6 @@ Errors: NotChanged: Email not changed Empty: Email is empty IDMissing: Email ID is missing - InvalidURLTemplate: URL Template is invalid Phone: NotFound: Phone not found Invalid: Phone is invalid diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index b40868ded..757a73054 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: El usuario no pudo encontrarse en la organización elegida NotAllowedOrg: El usuario no es miembro de la organización requerida UserIDMissing: Falta el ID de usuario + UserIDWrong: "Solicitud de usuario no igual al usuario autenticado" DomainPolicyNil: Falta la política de la organización EmailAsUsernameNotAllowed: La dirección de Email no se permite como nombre de usuario Invalid: Los datos de usuario no son válidos @@ -67,6 +68,7 @@ Errors: NoChanges: No se encontraron cambios InitCodeNotFound: Código de inicialización no encontrado UsernameNotChanged: El nombre de usuario no cambió + InvalidURLTemplate: La plantilla URL no es válida Profile: NotFound: Perfil no encontrado NotChanged: El perfil no ha cambiado @@ -81,7 +83,6 @@ Errors: NotChanged: El email no ha cambiado Empty: El email no está vacío IDMissing: Falta el ID del email - InvalidURLTemplate: La plantilla URL no es válida Phone: NotFound: Teléfono no encontrado Invalid: El teléfono no es válido diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index 2f3415f5a..eb5b5471e 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: L'utilisateur n'a pas été trouvé dans l'organisation choisie NotAllowedOrg: L'utilisateur n'est pas membre de l'organisation requise UserIDMissing: L'ID de l'utilisateur est manquant + UserIDWrong: "L'utilisateur de la demande n'est pas égal à l'utilisateur authentifié" DomainPolicyNil: La politique de l'organisation est vide EmailAsUsernameNotAllowed: L'email n'est pas autorisé comme nom d'utilisateur Invalid: Les données de l'utilisateur ne sont pas valides @@ -67,6 +68,7 @@ Errors: NoChanges: Aucun changement trouvé InitCodeNotFound: Code d'initialisation non trouvé UsernameNotChanged: Nom d'utilisateur non modifié + InvalidURLTemplate: Le modèle d'URL n'est pas valide Profile: NotFound: Profil non trouvé NotChanged: Le profil n'a pas changé @@ -81,7 +83,6 @@ Errors: NotChanged: L'adresse électronique n'a pas changé Empty: Email est vide IDMissing: Email ID manquant - InvalidURLTemplate: Le modèle d'URL n'est pas valide Phone: Notfound: Téléphone non trouvé Invalid: Le téléphone n'est pas valide diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 16aff8c8f..7ce4200b7 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: L'utente non è stato trovato nell'organizzazione scelta NotAllowedOrg: L'utente non è membro dell'organizzazione richiesta UserIDMissing: ID utente mancante + UserIDWrong: "Utente richiesta non uguale all'utente autenticato" DomainPolicyNil: Impostazione Org IAM mancante EmailAsUsernameNotAllowed: L'e-mail non è consentita come nome utente Invalid: I dati utente non sono validi @@ -67,6 +68,7 @@ Errors: NoChanges: Nessun cambiamento trovato InitCodeNotFound: Codice di inizializzazione non trovato UsernameNotChanged: Nome utente non cambiato + InvalidURLTemplate: Il modello di URL non è valido Profile: NotFound: Profilo non trovato NotChanged: Profilo non cambiato @@ -81,7 +83,6 @@ Errors: NotChanged: Email non cambiata Empty: Email è vuota IDMissing: Email ID mancante - InvalidURLTemplate: Il modello di URL non è valido Phone: NotFound: Telefono non trovato Invalid: Il telefono non è valido diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index a45a924a6..394880e11 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: ユーザーが選択した組織内で見つかりません NotAllowedOrg: ユーザーが必要な組織のメンバーでありません UserIDMissing: ユーザーIDがありません + UserIDWrong: "リクエストユーザーが認証されたユーザーと等しくない" DomainPolicyNil: 組織ポリシーが空です EmailAsUsernameNotAllowed: メールアドレスはユーザー名として使用できません Invalid: 無効なユーザーデータです @@ -67,6 +68,7 @@ Errors: NoChanges: 変更は見つかりません InitCodeNotFound: 初期化コードが見つかりません UsernameNotChanged: ユーザー名は変更されていません + InvalidURLTemplate: URLテンプレートが無効です Profile: NotFound: プロファイルが見つかりません NotChanged: プロファイルが変更されていません @@ -76,7 +78,6 @@ Errors: Invalid: 無効なメールアドレスです AlreadyVerified: メールアドレスはすでに検証済みです NotChanged: メールアドレスが変更されていません - InvalidURLTemplate: URLテンプレートが無効です Phone: NotFound: 電話番号が見つかりません Invalid: 無効な電話番号です diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index 9e7131d8d..3be75368b 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: Użytkownik nie został znaleziony w wybranej organizacji NotAllowedOrg: Użytkownik nie jest członkiem wymaganej organizacji UserIDMissing: Brakuje ID użytkownika + UserIDWrong: "Żądanie użytkownika nie jest równe uwierzytelnionemu użytkownikowi" DomainPolicyNil: Polityka organizacji jest pusta EmailAsUsernameNotAllowed: Adres e-mail nie jest dozwolony jako nazwa użytkownika Invalid: Dane użytkownika są nieprawidłowe @@ -67,6 +68,7 @@ Errors: NoChanges: Nie znaleziono zmian InitCodeNotFound: Kod inicjalizacji nie znaleziony UsernameNotChanged: Nazwa użytkownika nie została zmieniona + InvalidURLTemplate: Szablon URL jest nieprawidłowy Profile: NotFound: Profil nie znaleziony NotChanged: Profil nie zmieniony @@ -81,7 +83,6 @@ Errors: NotChanged: Adres e-mail nie zmieniony Empty: Adres e-mail jest pusty IDMissing: Adres e-mail ID brakuje - InvalidURLTemplate: Szablon URL jest nieprawidłowy Phone: NotFound: Numer telefonu nie znaleziony Invalid: Numer telefonu jest nieprawidłowy diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index 331c41db9..eb3a76c77 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -53,6 +53,7 @@ Errors: NotFoundOnOrg: 在所选组织中找不到用户 NotAllowedOrg: 用户不是所需组织的成员 UserIDMissing: 缺少用户 ID + UserIDWrong: "请求用户不等于经过身份验证的用户" DomainPolicyNil: 组织策略为空 EmailAsUsernameNotAllowed: 电子邮件不允许作为用户名 Invalid: 用户数据无效 @@ -67,6 +68,7 @@ Errors: NoChanges: 未发现任何更改 InitCodeNotFound: 未找到初始化验证码 UsernameNotChanged: 用户名未更改 + InvalidURLTemplate: URL模板无效 Profile: NotFound: 未找到个人资料 NotChanged: 个人资料未更改 @@ -81,7 +83,6 @@ Errors: NotChanged: 电子邮件未更改 Empty: 电子邮件是空的 IDMissing: 电子邮件ID丢失 - InvalidURLTemplate: URL模板无效 Phone: NotFound: 手机号码未找到 Invalid: 手机号码无效 diff --git a/internal/webauthn/client.go b/internal/webauthn/client.go new file mode 100644 index 000000000..ac7fbe766 --- /dev/null +++ b/internal/webauthn/client.go @@ -0,0 +1,36 @@ +package webauthn + +import ( + "fmt" + + "github.com/descope/virtualwebauthn" +) + +type Client struct { + rp virtualwebauthn.RelyingParty + auth virtualwebauthn.Authenticator + credential virtualwebauthn.Credential +} + +func NewClient(name, domain, origin string) *Client { + rp := virtualwebauthn.RelyingParty{ + Name: name, + ID: domain, + Origin: origin, + } + return &Client{ + rp: rp, + auth: virtualwebauthn.NewAuthenticator(), + credential: virtualwebauthn.NewCredential(virtualwebauthn.KeyTypeEC2), + } +} + +func (c *Client) CreateAttestationResponse(options []byte) ([]byte, error) { + parsedAttestationOptions, err := virtualwebauthn.ParseAttestationOptions(string(options)) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err) + } + return []byte(virtualwebauthn.CreateAttestationResponse( + c.rp, c.auth, c.credential, *parsedAttestationOptions, + )), nil +} diff --git a/internal/webauthn/converter.go b/internal/webauthn/converter.go index 924fbbb49..9b49ec016 100644 --- a/internal/webauthn/converter.go +++ b/internal/webauthn/converter.go @@ -1,8 +1,8 @@ package webauthn import ( - "github.com/duo-labs/webauthn/protocol" - "github.com/duo-labs/webauthn/webauthn" + "github.com/go-webauthn/webauthn/protocol" + "github.com/go-webauthn/webauthn/webauthn" "github.com/zitadel/zitadel/internal/domain" ) diff --git a/internal/webauthn/webauthn.go b/internal/webauthn/webauthn.go index d1a62819b..5d2517bc4 100644 --- a/internal/webauthn/webauthn.go +++ b/internal/webauthn/webauthn.go @@ -5,8 +5,8 @@ import ( "context" "encoding/json" - "github.com/duo-labs/webauthn/protocol" - "github.com/duo-labs/webauthn/webauthn" + "github.com/go-webauthn/webauthn/protocol" + "github.com/go-webauthn/webauthn/webauthn" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/api/authz" @@ -177,9 +177,13 @@ func (w *Config) FinishLogin(ctx context.Context, user *domain.Human, webAuthN * func (w *Config) serverFromContext(ctx context.Context) (*webauthn.WebAuthn, error) { instance := authz.GetInstance(ctx) - return webauthn.New(&webauthn.Config{ + webAuthn, err := webauthn.New(&webauthn.Config{ RPDisplayName: w.DisplayName, RPID: instance.RequestedDomain(), - RPOrigin: http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure), + RPOrigins: []string{http.BuildOrigin(instance.RequestedHost(), w.ExternalSecure)}, }) + if err != nil { + return nil, caos_errs.ThrowInternal(err, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig") + } + return webAuthn, nil } diff --git a/internal/webauthn/webauthn_test.go b/internal/webauthn/webauthn_test.go new file mode 100644 index 000000000..df05d34e9 --- /dev/null +++ b/internal/webauthn/webauthn_test.go @@ -0,0 +1,58 @@ +package webauthn + +import ( + "context" + "testing" + + "github.com/go-webauthn/webauthn/webauthn" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/internal/api/authz" + caos_errs "github.com/zitadel/zitadel/internal/errors" +) + +func TestConfig_serverFromContext(t *testing.T) { + type args struct { + ctx context.Context + } + tests := []struct { + name string + args args + want *webauthn.WebAuthn + wantErr error + }{ + { + name: "webauthn error", + args: args{context.Background()}, + wantErr: caos_errs.ThrowInternal(nil, "WEBAU-UX9ta", "Errors.User.WebAuthN.ServerConfig"), + }, + { + name: "success", + args: args{authz.WithRequestedDomain(context.Background(), "example.com")}, + want: &webauthn.WebAuthn{ + Config: &webauthn.Config{ + RPDisplayName: "DisplayName", + RPID: "example.com", + RPOrigins: []string{"https://example.com"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &Config{ + DisplayName: "DisplayName", + ExternalSecure: true, + } + got, err := w.serverFromContext(tt.args.ctx) + require.ErrorIs(t, err, tt.wantErr) + if tt.want != nil { + require.NotNil(t, got) + assert.Equal(t, tt.want.Config.RPDisplayName, got.Config.RPDisplayName) + assert.Equal(t, tt.want.Config.RPID, got.Config.RPID) + assert.Equal(t, tt.want.Config.RPOrigins, got.Config.RPOrigins) + } + }) + } +} diff --git a/proto/zitadel/user/v2alpha/auth.proto b/proto/zitadel/user/v2alpha/auth.proto index 754877fd4..92ac47b5f 100644 --- a/proto/zitadel/user/v2alpha/auth.proto +++ b/proto/zitadel/user/v2alpha/auth.proto @@ -20,7 +20,7 @@ message SendPasskeyRegistrationLink { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1; max_length: 200; - example: "\"https://example.com/passkey/register?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}\""; + example: "\"https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}\""; description: "\"Optionally set a url_template, which will be used in the mail sent by ZITADEL to guide the user to your passkey registration page. If no template is set, the default ZITADEL url will be used.\"" } ]; From b43f32eba19d0ac20549af42de74c5b8ae26d702 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Thu, 8 Jun 2023 10:13:25 +0200 Subject: [PATCH 16/76] chore: merge main --- .github/ISSUE_TEMPLATE/BUG_REPORT.yaml | 20 +- .github/ISSUE_TEMPLATE/docs.yaml | 31 ++ .github/ISSUE_TEMPLATE/improvement.yaml | 55 ++ .github/ISSUE_TEMPLATE/proposal.yaml | 55 ++ .github/ISSUE_TEMPLATE/task.md | 14 - .github/workflows/integration.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 4 +- .../idp-table/idp-table.component.html | 22 +- .../modules/idp-table/idp-table.component.ts | 22 +- console/src/app/services/overlay/workflows.ts | 14 + console/src/assets/i18n/de.json | 3 + console/src/assets/i18n/en.json | 5 +- console/src/assets/i18n/es.json | 3 + console/src/assets/i18n/fr.json | 3 + console/src/assets/i18n/it.json | 3 + console/src/assets/i18n/ja.json | 3 + console/src/assets/i18n/pl.json | 3 + console/src/assets/i18n/zh.json | 3 + docs/docs/apis/openidoauth/endpoints.mdx | 51 +- docs/docs/apis/saml/endpoints.md | 12 +- docs/docs/concepts/architecture/software.md | 2 +- docs/docs/concepts/architecture/solution.md | 2 +- docs/docs/concepts/features/audit-trail.md | 2 +- docs/docs/examples/sdks.md | 3 +- docs/docs/guides/migrate/introduction.md | 2 +- docs/docs/legal/acceptable-use-policy.md | 2 - .../legal/policies/account-lockout-policy.md | 62 +++ docs/docs/legal/rate-limit-policy.md | 1 - .../legal/vulnerability-disclosure-policy.mdx | 4 +- docs/docs/self-hosting/deploy/kubernetes.mdx | 45 +- docs/docs/self-hosting/deploy/overview.mdx | 2 +- .../manage/database/_postgres.mdx | 2 +- .../manage/productionchecklist.md | 14 +- docs/docs/support/technical_advisory.mdx | 7 + docs/docs/support/troubleshooting.md | 36 ++ docs/sidebars.js | 42 +- docs/src/pages/index.js | 2 +- internal/api/grpc/admin/idp.go | 17 + .../admin/information_integration_test.go | 3 +- internal/api/grpc/fields.go | 4 +- internal/api/grpc/management/idp.go | 17 + internal/api/grpc/management/user.go | 19 +- internal/api/grpc/session/v2/session.go | 64 ++- .../session/v2/session_integration_test.go | 270 ++++++++++ internal/api/grpc/session/v2/session_test.go | 53 +- .../grpc/user/v2/email_integration_test.go | 29 +- internal/api/grpc/user/v2/passkey.go | 15 +- .../grpc/user/v2/passkey_integration_test.go | 26 +- internal/api/grpc/user/v2/passkey_test.go | 41 +- .../api/grpc/user/v2/user_integration_test.go | 2 +- .../api/ui/login/external_provider_handler.go | 16 +- .../login/static/resources/scripts/base64.js | 68 --- .../login/static/resources/scripts/utils.js | 63 +++ .../static/resources/scripts/webauthn.js | 39 +- .../resources/scripts/webauthn_login.js | 77 +-- .../resources/scripts/webauthn_register.js | 83 ++-- .../resources/themes/zitadel/css/zitadel.css | 8 +- .../themes/zitadel/css/zitadel.css.map | 2 +- .../login/static/templates/mfa_init_u2f.html | 2 +- .../templates/mfa_verification_u2f.html | 2 +- .../login/static/templates/passwordless.html | 2 +- .../templates/passwordless_registration.html | 2 +- internal/command/idp_model.go | 12 + internal/command/instance_idp.go | 141 +++++- internal/command/instance_idp_model.go | 12 + internal/command/instance_idp_test.go | 468 ++++++++++++++++++ internal/command/org_idp.go | 118 +++++ internal/command/org_idp_model.go | 12 + internal/command/org_idp_test.go | 468 ++++++++++++++++++ internal/command/session.go | 59 ++- internal/command/session_model.go | 54 ++ internal/command/session_passkey.go | 84 ++++ internal/command/session_passkeys_test.go | 130 +++++ internal/command/session_test.go | 138 +++++- internal/command/user_machine_secret.go | 2 - internal/command/user_machine_secret_test.go | 2 - internal/domain/idp.go | 3 +- internal/eventstore/repository/sql/crdb.go | 2 +- internal/integration/assert.go | 2 +- internal/integration/client.go | 77 +++ internal/integration/integration.go | 15 +- internal/query/projection/idp_template.go | 104 ++++ .../query/projection/idp_template_test.go | 272 ++++++++++ internal/query/projection/session.go | 28 +- internal/query/projection/session_test.go | 14 +- internal/query/session.go | 17 + internal/query/sessions_test.go | 78 +-- internal/repository/idp/oidc.go | 90 ++++ internal/repository/instance/eventstore.go | 2 + internal/repository/instance/idp.go | 86 ++++ internal/repository/org/eventstore.go | 2 + internal/repository/org/idp.go | 86 ++++ internal/repository/session/eventstore.go | 2 + internal/repository/session/session.go | 89 +++- internal/static/i18n/de.yaml | 2 + internal/static/i18n/en.yaml | 2 + internal/static/i18n/es.yaml | 2 + internal/static/i18n/fr.yaml | 2 + internal/static/i18n/it.yaml | 2 + internal/static/i18n/ja.yaml | 2 + internal/static/i18n/pl.yaml | 2 + internal/static/i18n/zh.yaml | 2 + internal/webauthn/client.go | 36 +- proto/zitadel/admin.proto | 35 ++ proto/zitadel/management.proto | 35 ++ proto/zitadel/session/v2alpha/challenge.proto | 26 + proto/zitadel/session/v2alpha/session.proto | 10 +- .../session/v2alpha/session_service.proto | 23 + proto/zitadel/user/v2alpha/user_service.proto | 14 +- 110 files changed, 3883 insertions(+), 465 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/docs.yaml create mode 100644 .github/ISSUE_TEMPLATE/improvement.yaml create mode 100644 .github/ISSUE_TEMPLATE/proposal.yaml delete mode 100644 .github/ISSUE_TEMPLATE/task.md create mode 100644 docs/docs/legal/policies/account-lockout-policy.md create mode 100644 internal/api/grpc/session/v2/session_integration_test.go delete mode 100644 internal/api/ui/login/static/resources/scripts/base64.js create mode 100644 internal/api/ui/login/static/resources/scripts/utils.js create mode 100644 internal/command/session_passkey.go create mode 100644 internal/command/session_passkeys_test.go create mode 100644 internal/integration/client.go create mode 100644 proto/zitadel/session/v2alpha/challenge.proto diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml index f45c602c8..a5a562ef7 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yaml @@ -1,7 +1,7 @@ name: Bug Report description: "Create a bug report to help us improve ZITADEL. Click [here](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#product-management) to see how we process your issue." title: "[Bug]: " -labels: ["type: bug", "state: triage"] +labels: ["bug"] body: - type: markdown attributes: @@ -27,11 +27,16 @@ body: - Self-hosted validations: required: true -- type: textarea - id: description +- type: input + id: version attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. + label: Version + description: Which version of ZITADEL are you using. +- type: textarea + id: impact + attributes: + label: Describe the problem caused by this bug + description: A clear and concise description of the problem you have and what the bug is. validations: required: true - type: textarea @@ -57,11 +62,6 @@ body: attributes: label: Expected behavior description: A clear and concise description of what you expected to happen. -- type: input - id: version - attributes: - label: Version - description: Which version of ZITADEL are you using. - type: textarea id: os attributes: diff --git a/.github/ISSUE_TEMPLATE/docs.yaml b/.github/ISSUE_TEMPLATE/docs.yaml new file mode 100644 index 000000000..777692f11 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs.yaml @@ -0,0 +1,31 @@ +name: 📄 Documentation +description: Create an issue for missing or wrong documentation. +title: +labels: ["docs"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this issue. + - type: checkboxes + id: preflight + attributes: + label: Preflight Checklist + options: + - label: + I could not find a solution in the existing issues, docs, nor discussions + required: true + - label: + I have joined the [ZITADEL chat](https://zitadel.com/chat) + - type: textarea + id: docs + attributes: + label: Describe the docs your are missing or that are wrong + placeholder: As a [type of user], I want [some goal] so that [some reason]. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: Please add any other infos that could be useful. diff --git a/.github/ISSUE_TEMPLATE/improvement.yaml b/.github/ISSUE_TEMPLATE/improvement.yaml new file mode 100644 index 000000000..1cd864755 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement.yaml @@ -0,0 +1,55 @@ +name: 🛠️ Improvement +description: +title: +labels: ["improvement"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this improvement request + - type: checkboxes + id: preflight + attributes: + label: Preflight Checklist + options: + - label: + I could not find a solution in the existing issues, docs, nor discussions + required: true + - label: + I have joined the [ZITADEL chat](https://zitadel.com/chat) + - type: textarea + id: problem + attributes: + label: Describe your problem + description: Please describe your problem this improvement is supposed to solve. + placeholder: Describe the problem you have + validations: + required: true + - type: textarea + id: solution + attributes: + label: Describe your ideal solution + description: Which solution do you propose? + placeholder: As a [type of user], I want [some goal] so that [some reason]. + validations: + required: true + - type: input + id: version + attributes: + label: Version + description: Which version of the ZITADEL are you using. + - type: dropdown + id: environment + attributes: + label: Environment + description: How do you use ZITADEL? + options: + - ZITADEL Cloud + - Self-hosted + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: Please add any other infos that could be useful. diff --git a/.github/ISSUE_TEMPLATE/proposal.yaml b/.github/ISSUE_TEMPLATE/proposal.yaml new file mode 100644 index 000000000..29b08db5c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/proposal.yaml @@ -0,0 +1,55 @@ +name: 💡 Proposal / Feature request +description: +title: +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this proposal / feature reqeust + - type: checkboxes + id: preflight + attributes: + label: Preflight Checklist + options: + - label: + I could not find a solution in the existing issues, docs, nor discussions + required: true + - label: + I have joined the [ZITADEL chat](https://zitadel.com/chat) + - type: textarea + id: problem + attributes: + label: Describe your problem + description: Please describe your problem this proposal / feature is supposed to solve. + placeholder: Describe the problem you have. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Describe your ideal solution + description: Which solution do you propose? + placeholder: As a [type of user], I want [some goal] so that [some reason]. + validations: + required: true + - type: input + id: version + attributes: + label: Version + description: Which version of ZITADEL are you using. + - type: dropdown + id: environment + attributes: + label: Environment + description: How do you use ZITADEL? + options: + - ZITADEL Cloud + - Self-hosted + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: Please add any other infos that could be useful. diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md deleted file mode 100644 index 7ea32464e..000000000 --- a/.github/ISSUE_TEMPLATE/task.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: User Story -about: A user story is a brief description of a feature that has to be implemented from the perspective of the end user. -title: '' -assignees: '' - ---- - -As a [type of user], I want [some goal] so that [some reason]. - -```[tasklist] -### Acceptance Criteria -- [ ] ... -``` diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 2347e029a..23e35ceb6 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -43,7 +43,7 @@ jobs: go run main.go init --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml go run main.go setup --masterkeyFromEnv --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml - name: Run integration tests - run: go test -tags=integration -race -parallel 1 -v -coverprofile=profile.cov -coverpkg=./internal/...,./cmd/... ./internal/integration ./internal/api/grpc/... + run: go test -tags=integration -race -p 1 -v -coverprofile=profile.cov -coverpkg=./internal/...,./cmd/... ./internal/integration ./internal/api/grpc/... - name: Publish go coverage uses: codecov/codecov-action@v3.1.0 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00238fd51..06be1797a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -208,7 +208,7 @@ export INTEGRATION_DB_FLAVOR="cockroach" ZITADEL_MASTERKEY="MasterkeyNeedsToHave docker compose -f internal/integration/config/docker-compose.yaml up --wait ${INTEGRATION_DB_FLAVOR} go run main.go init --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml go run main.go setup --masterkeyFromEnv --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml -go test -tags=integration -race -parallel 1 ./internal/integration ./internal/api/grpc/... +go test -count 1 -tags=integration -race -p 1 ./internal/integration ./internal/api/grpc/... docker compose -f internal/integration/config/docker-compose.yaml down ``` diff --git a/README.md b/README.md index bfabb60be..d92927437 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,9 @@ Use [Console](https://zitadel.com/docs/guides/manage/console/overview) or our [A ## Security -See the policy [here](./SECURITY.md) +See the policy [here](./SECURITY.md). + +[Technical Advisories](https://zitadel.com/docs/support/technical_advisory) are published regarding major issues with the ZITADEL platform that could potentially impact security or stability in production environments. ## License diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 111023285..2990f196d 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -13,7 +13,7 @@ {{ 'IDP.AVAILABILITY' | translate }} - + {{ 'IDP.NAME' | translate }} - + {{ idp?.name }} {{ 'IDP.TYPE' | translate }} - +
@@ -87,7 +87,7 @@ {{ 'IDP.STATE' | translate }} - + {{ 'IDP.CREATIONDATE' | translate }} - + {{ idp.details.creationDate | timestampToDate | localizedDate : 'dd. MMM, HH:mm' }} {{ 'IDP.CHANGEDATE' | translate }} - + {{ idp.details.changeDate | timestampToDate | localizedDate : 'dd. MMM, HH:mm' }} {{ 'IDP.OWNER' | translate }} - + {{ 'IDP.OWNERTYPES.' + idp.owner | translate }} @@ -140,7 +140,7 @@ " mat-icon-button matTooltip="{{ 'IDP.SETAVAILABLE' | translate }}" - (click)="addIdp(idp)" + (click)="addIdp(idp); $event.stopPropagation()" > @@ -160,7 +160,7 @@ " mat-icon-button matTooltip="{{ 'IDP.SETUNAVAILABLE' | translate }}" - (click)="removeIdp(idp)" + (click)="removeIdp(idp); $event.stopPropagation()" > @@ -185,7 +185,7 @@ mat-icon-button color="warn" matTooltip="{{ 'ACTIONS.REMOVE' | translate }}" - (click)="deleteIdp(idp)" + (click)="deleteIdp(idp); $event.stopPropagation()" > @@ -194,7 +194,7 @@ - +
diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index 4719f282a..6df954413 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -2,7 +2,7 @@ import { SelectionModel } from '@angular/cdk/collections'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table'; -import { RouterLink } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -31,6 +31,8 @@ import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { OverlayWorkflowService } from 'src/app/services/overlay/overlay-workflow.service'; +import { ContextChangedWorkflowOverlays } from 'src/app/services/overlay/workflows'; import { PageEvent, PaginatorComponent } from '../paginator/paginator.component'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; @@ -60,7 +62,13 @@ export class IdpTableComponent implements OnInit { public IDPStylingType: any = IDPStylingType; public loginPolicy!: LoginPolicy.AsObject; - constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { + constructor( + private workflowService: OverlayWorkflowService, + public translate: TranslateService, + private toast: ToastService, + private dialog: MatDialog, + private router: Router, + ) { this.selection.changed.subscribe(() => { this.changedSelection.emit(this.selection.selected); }); @@ -241,6 +249,16 @@ export class IdpTableComponent implements OnInit { } } + navigateToIDP(row: Provider.AsObject) { + this.router.navigate(this.routerLinkForRow(row)).then(() => { + if (this.serviceType === PolicyComponentServiceType.MGMT && row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM) { + setTimeout(() => { + this.workflowService.startWorkflow(ContextChangedWorkflowOverlays, null); + }, 1000); + } + }); + } + private async getIdps(): Promise { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: diff --git a/console/src/app/services/overlay/workflows.ts b/console/src/app/services/overlay/workflows.ts index 1c6c8263b..6f9c11e56 100644 --- a/console/src/app/services/overlay/workflows.ts +++ b/console/src/app/services/overlay/workflows.ts @@ -66,3 +66,17 @@ export const OrgContextChangedWorkflowOverlays: CnslOverlay[] = [ }, }, ]; + +export const ContextChangedWorkflowOverlays: CnslOverlay[] = [ + { + id: 'contextswitcher', + origin: 'orgbutton', + toHighlight: ['orgbutton'], + content: { + i18nText: 'OVERLAYS.SWITCHEDTOINSTANCE.TEXT', + }, + requirements: { + permission: ['iam.read'], + }, + }, +]; diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 68d81ac77..832ce451b 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -204,6 +204,9 @@ }, "CONTEXTCHANGED": { "TEXT": "Achtung! Soeben wurde die Organisation gewechselt." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "Soeben wurde die Ansicht auf Instanz gewechselt." } }, "FILTER": { diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 2d27939ee..d6a19679e 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -204,7 +204,10 @@ "TEXT": "This navigation changes based on your selected organization above or your instance" }, "CONTEXTCHANGED": { - "TEXT": "Attention! The organization context has changed." + "TEXT": "The organization context has changed." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "The view just changed to instance!" } }, "FILTER": { diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index 0fea6748a..8e1164d09 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -205,6 +205,9 @@ }, "CONTEXTCHANGED": { "TEXT": "¡Atención! El contexto de la organización ha cambiado." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "¡La vista acaba de cambiar a instancia!" } }, "FILTER": { diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index e24116fd8..07e0829e8 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -204,6 +204,9 @@ }, "CONTEXTCHANGED": { "TEXT": "Attention ! Le contexte de l'organisation a changé." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "La vue vient de changer en instance !" } }, "FILTER": { diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 4d18722af..e025b4839 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -204,6 +204,9 @@ }, "CONTEXTCHANGED": { "TEXT": "Attenzione! L'organizzazione è appena stata cambiata." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "La visualizzazione è appena stata modificata in istanza!" } }, "FILTER": { diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 2ba3863ad..6dea6b8db 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -205,6 +205,9 @@ }, "CONTEXTCHANGED": { "TEXT": "注意! 組織のコンテキストが変更されました。" + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "ビューがインスタンスに変更されました。" } }, "FILTER": { diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 0dd538ba3..3d9a3f825 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -204,6 +204,9 @@ }, "CONTEXTCHANGED": { "TEXT": "Uwaga! Kontekst organizacji uległ zmianie." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "Widok właśnie zmienił się na instancję!" } }, "FILTER": { diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index dbc8dcc19..b8f735d51 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -204,6 +204,9 @@ }, "CONTEXTCHANGED": { "TEXT": "注意!组织环境发生了变化。" + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "视图刚刚更改为实例!" } }, "FILTER": { diff --git a/docs/docs/apis/openidoauth/endpoints.mdx b/docs/docs/apis/openidoauth/endpoints.mdx index 8c8a3dc48..8e9b8b231 100644 --- a/docs/docs/apis/openidoauth/endpoints.mdx +++ b/docs/docs/apis/openidoauth/endpoints.mdx @@ -1,5 +1,5 @@ --- -title: Endpoints +title: OpenID Connect Endpoints --- import Tabs from "@theme/Tabs"; @@ -101,7 +101,7 @@ no additional parameters required | state | Opaque value used to maintain state between the request and the callback. Used for Cross-Site Request Forgery (CSRF) mitigation as well, therefore highly **recommended**. | | ui_locales | Spaces delimited list of preferred locales for the login UI, e.g. `de-CH de en`. If none is provided or matches the possible locales provided by the login UI, the `accept-language` header of the browser will be taken into account. | -### Successful Code Response +### Successful code response When your `response_type` was `code` and no error occurred, the following response will be returned: @@ -110,7 +110,7 @@ When your `response_type` was `code` and no error occurred, the following respon | code | Opaque string which will be necessary to request tokens on the token endpoint | | state | Unmodified `state` parameter from the request | -### Successful Implicit Response +### Successful implicit response When your `response_type` was either `it_token` or `id_token token` and no error occurred, the following response will be returned: @@ -123,7 +123,7 @@ When your `response_type` was either `it_token` or `id_token token` and no error | scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. | | state | Unmodified `state` parameter from the request | -### Error Response +### Error response Regardless of the authorization flow chosen, if an error occurs the following response will be returned to the redirect_uri. @@ -158,11 +158,11 @@ The token_endpoint will as the name suggests return various tokens (access, id a When using [`authorization_code`](#authorization-code-grant-code-exchange) flow call this endpoint after receiving the code from the authorization_endpoint. When using [`refresh_token`](#authorization-code-grant-code-exchange) or [`urn:ietf:params:oauth:grant-type:jwt-bearer` (JWT Profile)](#jwt-profile-grant) you will call this endpoint directly. -### Authorization Code Grant (Code Exchange) +### Authorization code grant (Code Exchange) As mention above, when using `authorization_code` grant, this endpoint will be your second request for authorizing a user with its user agent (browser). -#### Required request Parameters +#### Required request parameters | Parameter | Description | | ------------ | ------------------------------------------------------------------------------------------------------------- | @@ -229,9 +229,9 @@ Send a client assertion as JWT for us to validate the signature against the regi | refresh_token | An opaque token. Only returned if `offline_access` scope was requested | | token_type | Type of the `access_token`. Value is always `Bearer` | -### JWT Profile Grant +### JWT profile grant -#### Required request Parameters +#### Required request parameters | Parameter | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------- | @@ -247,7 +247,7 @@ curl --request POST \ --data assertion=eyJhbGciOiJSUzI1Ni... ``` -#### Successful JWT Profile response {#token-jwt-response} +#### Successful JWT profile response {#token-jwt-response} | Property | Description | | ------------ | ------------------------------------------------------------------------------------- | @@ -257,12 +257,12 @@ curl --request POST \ | scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. | | token_type | Type of the `access_token`. Value is always `Bearer` | -### Refresh Token Grant +### Refresh token grant To request a new `access_token` without user interaction, you can use the `refresh_token` grant. See [offline_access Scope](scopes#standard-scopes) for how to request a `refresh_token` in the authorization request. -#### Required request Parameters +#### Required request parameters | Parameter | Description | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -325,9 +325,9 @@ Send a `client_assertion` as JWT for us to validate the signature against the re | refresh_token | An new opaque refresh_token. | | token_type | Type of the `access_token`. Value is always `Bearer` | -### Client Credentials Grant +### Client credentials grant -#### Required request Parameters +#### Required request parameters | Parameter | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------- | @@ -363,7 +363,7 @@ curl --request POST \ --data scope=openid profile ``` -#### Successful Client Credentials response {#token-client-credentials-response} +#### Successful client credentials response {#token-client-credentials-response} | Property | Description | | ------------ | ------------------------------------------------------------------------------------- | @@ -584,8 +584,27 @@ If both parameters are provided, they must be equal. {your_domain}/oauth/v2/keys -> Be aware that these keys can be rotated without any prior notice. We will however make sure that a proper `kid` is set with each key! +The endpoint returns a JSON Web Key Set (JWKS) containing the public keys that can be used to locally validate JWTs you received from ZITADEL. +The alternative would be to validate tokens with the [introspection endpoint](#introspection_endpoint). -## OAuth 2.0 Metadata +### Key rotation + +Keys are automatically rotated on a regular basis or on demand, meaning keys can change in irregular intervals. +ZITADEL ensures that a proper `kid` is set with each key. + +:::info Keys rotate without prior notice +Be aware that these keys can be rotated without any prior notice. +::: + +### Caching + +You can optimize performance of your clients by caching the response from the keys endpoint. +We recommend to regularly update the cached response, since the [keys can be rotated without prior notice](#key-rotation). +You could also combine caching with a risk-based on-demand refresh when a critical operation is executed. + +Without caching you will call this endpoint on each request. +This might result in being rate limited for a large number of requests that come from the same backend. + +## OAuth 2.0 metadata **ZITADEL** does not yet provide a OAuth 2.0 Metadata endpoint but instead provides a [OpenID Connect Discovery Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html). diff --git a/docs/docs/apis/saml/endpoints.md b/docs/docs/apis/saml/endpoints.md index 4cd186510..eeae326d9 100644 --- a/docs/docs/apis/saml/endpoints.md +++ b/docs/docs/apis/saml/endpoints.md @@ -1,8 +1,8 @@ --- -title: Endpoints +title: SAML endpoints --- -## SAML 2.0 Metadata +## SAML 2.0 metadata The SAML Metadata is located within the issuer domain. This would give us {your_domain}/saml/v2/metadata. @@ -11,14 +11,14 @@ This metadata contains all the information defined in the spec. **Link to spec.** [Metadata for the OASIS Security Assertion Markup Language (SAML) V2.0 – Errata Composite](https://www.oasis-open.org/committees/download.php/35391/sstc-saml-metadata-errata-2.0-wd-04-diff.pdf) -## Certificate Endpoint +## Certificate endpoint {your_domain}/saml/v2/certificate The certificate endpoint provides the certificate which is used to sign the responses for download, for easier use with different service providers which want the certificate separately instead of inside the metadata. -## SSO Endpoint +## SSO endpoint {your_domain}/saml/v2/SSO @@ -40,7 +40,7 @@ spec.** [Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0 | SigAlg | Algorithm used to sign the request, only if binding is 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' as signature has to be provided es separate parameter. (base64 encoded) | | Signature | Signature of the request as parameter with 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' binding. (base64 encoded) | -### Successful Response +### Successful response Depending on the content of the request the response comes back in the requested binding, but the content is the same. @@ -51,7 +51,7 @@ Depending on the content of the request the response comes back in the requested | SigAlg | Algorithm used to sign the response, only if binding is 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' as signature has to be provided es separate parameter. (base64 encoded) | | Signature | Signature of the response as parameter with 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' binding. (base64 encoded) | -### Error Response +### Error response Regardless of the error, the used http error code will be '200', which represents a successful request. Whereas the response will contain a StatusCode include a message which provides more information if an error occurred. diff --git a/docs/docs/concepts/architecture/software.md b/docs/docs/concepts/architecture/software.md index 29dcb2c1e..565221774 100644 --- a/docs/docs/concepts/architecture/software.md +++ b/docs/docs/concepts/architecture/software.md @@ -147,5 +147,5 @@ The storage layer of ZITADEL is responsible for multiple things. For example: ZITADEL currently supports CockroachDB as first choice of storage due to its perfect match for ZITADELs needs. Postgres is currently in [Beta](/docs/support/software-release-cycles-support#beta) and will be [Enterprise Supported](/docs/support/software-release-cycles-support#partially-supported) afterwards. Beta state will be removed as soon as [automated tests](https://github.com/zitadel/zitadel/issues/5741) are implemented. -Make sure to read our [Production Guide](./self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. +Make sure to read our [Production Guide](/docs/self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. diff --git a/docs/docs/concepts/architecture/solution.md b/docs/docs/concepts/architecture/solution.md index 176c15173..9b0656c98 100644 --- a/docs/docs/concepts/architecture/solution.md +++ b/docs/docs/concepts/architecture/solution.md @@ -11,7 +11,7 @@ Depending on your projects needs our general recommendation is to run ZITADEL an Consult the [CockroachDB documentation](https://www.cockroachlabs.com/docs/) for more details or use the [CockroachCloud Service](https://www.cockroachlabs.com/docs/cockroachcloud/create-an-account.html) Postgres is currently in [Beta](/docs/support/software-release-cycles-support#beta) and will be [Enterprise Supported](/docs/support/software-release-cycles-support#partially-supported) afterwards. Beta state will be removed as soon as [automated tests](https://github.com/zitadel/zitadel/issues/5741) are implemented. -Make sure to read our [Production Guide](./self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. +Make sure to read our [Production Guide](/self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. ## Scalability diff --git a/docs/docs/concepts/features/audit-trail.md b/docs/docs/concepts/features/audit-trail.md index bf1992453..12cd77606 100644 --- a/docs/docs/concepts/features/audit-trail.md +++ b/docs/docs/concepts/features/audit-trail.md @@ -4,7 +4,7 @@ title: Audit Trail ZITADEL provides you with an built-in audit trail to track all changes and events over an unlimited period of time. Most other solutions replace a historic record and track changes in a separate log when information is updated. -ZITADEL only ever appends data in an [Eventstore](https://zitadel.com/docs/concepts/eventstore), keeping all historic record. +ZITADEL only ever appends data in an [Eventstore](/docs/concepts/eventstore/overview), keeping all historic record. The audit trail itself is identical to the state, since ZITADEL calculates the state from all the past changes. ![Example of events that happen for a profile change and a login](/img/concepts/audit-trail/audit-log-events.png) diff --git a/docs/docs/examples/sdks.md b/docs/docs/examples/sdks.md index 6c70dba16..fc868a08a 100644 --- a/docs/docs/examples/sdks.md +++ b/docs/docs/examples/sdks.md @@ -94,5 +94,4 @@ You might want to check out the following links to find a good library: - [awesome-auth](https://github.com/casbin/awesome-auth) - [OpenID General References](https://openid.net/developers/libraries/) -- [OpenID certified libraries](https://openid.net/developers/certified/) -- [OpenID uncertified libraries](https://openid.net/developers/uncertified/) \ No newline at end of file +- [OpenID certified developer tools](https://openid.net/certified-open-id-developer-tools/) \ No newline at end of file diff --git a/docs/docs/guides/migrate/introduction.md b/docs/docs/guides/migrate/introduction.md index 887a374a2..6ac351f43 100644 --- a/docs/docs/guides/migrate/introduction.md +++ b/docs/docs/guides/migrate/introduction.md @@ -8,7 +8,7 @@ The individual guides in this section should give you an overview of things to c When moving from a previous auth solution to ZITADEL, it is important to note that some decisions and features are unique to ZITADEL. Without duplicating too much content here are some important features and patterns to consider in terms of solution architecture. -You can read more about the basic structure and important concepts of ZITADEL in our [concepts section](https://zitadel.com/docs/concepts/introduction). +You can read more about the basic structure and important concepts of ZITADEL in our [concepts section](/docs/concepts/). ## Multi-Tenancy Architecture diff --git a/docs/docs/legal/acceptable-use-policy.md b/docs/docs/legal/acceptable-use-policy.md index 683bca365..5fb99138a 100644 --- a/docs/docs/legal/acceptable-use-policy.md +++ b/docs/docs/legal/acceptable-use-policy.md @@ -3,8 +3,6 @@ title: Acceptable Use Policy custom_edit_url: null --- -## Introduction - This policy is an annex to the [Terms of Service](terms-of-service) and clarifies your obligations while using our Services. ## Use diff --git a/docs/docs/legal/policies/account-lockout-policy.md b/docs/docs/legal/policies/account-lockout-policy.md new file mode 100644 index 000000000..14463392d --- /dev/null +++ b/docs/docs/legal/policies/account-lockout-policy.md @@ -0,0 +1,62 @@ +--- +title: Account Lockout Policy +custom_edit_url: null +--- + +This policy is an annex to the [Terms of Service](../terms-of-service) that clarifies your obligations and our procedure handling requests where you can't get access to your ZITADEL Cloud services and data. This policy is applicable to situations where we, ZITADEL, need to restore your access for a otherwise available service and not in cases where the services are unavailable. + +## Why to do we have this policy? + +Users may not be able to access our services anymore due to loss of credentials or misconfiguration. +In certain circumstances it might not be possible to recover the credentials through a self-service flow (eg, loss of 2FA credentials) or access the system to undo the configuration that caused the issue. +These cases might require help from our support, so you can regain access to your data. + +We will require some initial information and conditions to be able to assist you, and will require further information to handle the request. +We also keep the right to refuse any such request without providing a reason, in case you can't provide the requested information. + +## Scope + +In scope of this policy are requests to recover + +- ZITADEL Cloud account (customer portal) +- Manager accounts to a specific instance +- Undo configuration changes resulting in lockout (eg, misconfigured Action) + +Out of scope are requests to recover access + +- Where you have to option to ask another Admin/Manager +- by end-users who should ask an Admin/Manager instead +- self-hosted instances + +## Process + +Before you send a request to restore access to your account, please make sure that can't ask your manager/admin or another manager/admin to recover access. + +### ZITADEL Cloud account + +If you need to recover your ZITADEL Cloud account for the customer portal, please send an email to [support@zitadel.com](mailto:support@zitadel.com?subject=ZITADEL%20Cloud%20account%20lockout): + +- State clearly in the subject line that this is related to an account lockout for a ZITADEL Cloud account +- The sender's email address must match the verified email address of the account owner +- State the reason why you're not able to recover the account yourself + +Please allow us time to validate your request. +Our support will get back to you to request additional information for verification. + +### Manager access to an Instance + +If you need to recover a Manager account to an instance, please make sure you can't recover the account via another user or service user with Manager permissions. + +Please visit the [support page in the customer portal](https://zitadel.cloud/admin/support): + +- State clearly in the subject line that this is related to an account lockout the affected instance +- State the reason why you're not able to recover the account yourself + +Please allow us time to validate your request. +Our support will get back to you to request additional information for verification. + +## Entry into force + +This policy is valid from May 31, 2023. + +Last revised May 31, 2023 \ No newline at end of file diff --git a/docs/docs/legal/rate-limit-policy.md b/docs/docs/legal/rate-limit-policy.md index 8cae76e99..48c78b6a4 100644 --- a/docs/docs/legal/rate-limit-policy.md +++ b/docs/docs/legal/rate-limit-policy.md @@ -2,7 +2,6 @@ title: Rate Limit Policy custom_edit_url: null --- -## Introduction This policy is an annex to the [Terms of Service](terms-of-service) and clarifies your obligations while using our Services, specifically how we will use rate limiting to enforce certain aspects of our [Acceptable Use Policy](acceptable-use-policy). diff --git a/docs/docs/legal/vulnerability-disclosure-policy.mdx b/docs/docs/legal/vulnerability-disclosure-policy.mdx index 7b6cc85eb..74d04258e 100644 --- a/docs/docs/legal/vulnerability-disclosure-policy.mdx +++ b/docs/docs/legal/vulnerability-disclosure-policy.mdx @@ -3,8 +3,6 @@ title: Vulnerability Disclosure Policy custom_edit_url: null --- -## Introduction - At ZITADEL we are extremely grateful for security aware people who disclose vulnerabilities to us and the open source community. All reports will be investigated by our team and we will work with you closely to validate and fix vulnerabilities reported to us. @@ -91,6 +89,6 @@ In case we have confirmed your report, we may compensate you, given prior writte ## Entry into force -This privacy policy is valid from March 16, 2023. +This policy is valid from March 16, 2023. Last revised March 16, 2023 diff --git a/docs/docs/self-hosting/deploy/kubernetes.mdx b/docs/docs/self-hosting/deploy/kubernetes.mdx index 7d3385666..fa214d577 100644 --- a/docs/docs/self-hosting/deploy/kubernetes.mdx +++ b/docs/docs/self-hosting/deploy/kubernetes.mdx @@ -10,49 +10,61 @@ Installation and configuration details are described in the [open source ZITADEL By default, the chart installs a secure and highly available ZITADEL instance. For running an easily testable, insecure, non-HA ZITADEL instance, run the following commands. -## Helm -### Add the helm repositories for CockroachDB and ZITADEL +## Add the Helm Repositories for CockroachDB and ZITADEL ```bash helm repo add cockroachdb https://charts.cockroachdb.com/ helm repo add zitadel https://charts.zitadel.com ``` -### Install zitadel +After you have your repositories added, +you can setup ZITADEL and either +- initialize an [IAM owner who is a human user](#setup-zitadel-and-a-human-admin) or +- initialize an [IAM owner who is a service account](#setup-zitadel-and-a-service-account-admin) -#### Install an insecure cockroachdb and zitadel release that works with localhost +## Setup ZITADEL and a Human Admin ```bash -# CockroachDB +# Install CockroachDB helm install crdb cockroachdb/cockroachdb \ --set fullnameOverride=crdb \ --set single-node=true \ --set statefulset.replicas=1 -# ZITADEL +# Install ZITADEL helm install my-zitadel zitadel/zitadel \ --set zitadel.masterkey="MasterkeyNeedsToHave32Characters" \ --set zitadel.configmapConfig.ExternalSecure=false \ --set zitadel.configmapConfig.TLS.Enabled=false \ --set zitadel.secretConfig.Database.cockroach.User.Password="a-zitadel-db-user-password" \ --set replicaCount=1 + +# Make ZITADEL locally accessible +kubectl port-forward svc/my-zitadel 8080 ``` -#### Install an insecure zitadel release that works with localhost with a service account +## Setup ZITADEL and a Service Account Admin -!!!Caution!!! With this setup you only get a service account with a key and no admin account where you can login directly into ZITADEL. +With this setup, you don't create a human user that has the IAM_OWNER role. +Instead, you create a service account that has the IAM_OWNER role. +ZITADEL will also create a key for your, with which you can authenticate to the ZITADEL API. +For example, you can install ZITADEL and seemlessly provision ZITADEL resources after installation using [Terraform](/docs/guides/manage/terraform/basics.md). + +:::caution +With this setup you only get a key for a service account. Logging in at ZITADEL using the login screen is not possible until you create a user with the ZITADEL API. +::: ```bash -# CockroachDB +# Install CockroachDB helm install crdb cockroachdb/cockroachdb \ --set fullnameOverride=crdb \ --set single-node=true \ --set statefulset.replicas=1 -# ZITADEL +# Install ZITADEL helm install --namespace zitadel --create-namespace my-zitadel zitadel/zitadel \ --set zitadel.masterkey="MasterkeyNeedsToHave32Characters" \ --set zitadel.configmapConfig.ExternalSecure=false \ @@ -63,20 +75,15 @@ helm install --namespace zitadel --create-namespace my-zitadel zitadel/zitadel \ --set zitadel.configmapConfig.FirstInstance.Org.Machine.Machine.Username="zitadel-admin-sa" \ --set zitadel.configmapConfig.FirstInstance.Org.Machine.Machine.Name="Admin" \ --set zitadel.configmapConfig.FirstInstance.Org.Machine.MachineKey.Type=1 + +# Make ZITADEL locally accessible +kubectl port-forward svc/my-zitadel 8080 ``` -When helm is done, you get a command to retrieve your machine key, which is saved as a kubernetes secret, for example: +When Helm is done, you can print your service account key from a Kubernetes secret: ```bash kubectl -n zitadel get secret zitadel-admin-sa -o jsonpath='{ .data.zitadel-admin-sa\.json }' | base64 -D ``` -This key can be used to provision resources with for example [Terraform](/docs/guides/manage/terraform/basics.md). - -### Forward the ZITADEL service port to your local machine - -```bash -kubectl port-forward svc/my-zitadel 8080:8080 -``` - diff --git a/docs/docs/self-hosting/deploy/overview.mdx b/docs/docs/self-hosting/deploy/overview.mdx index 8797c4359..d7ba3c92e 100644 --- a/docs/docs/self-hosting/deploy/overview.mdx +++ b/docs/docs/self-hosting/deploy/overview.mdx @@ -14,7 +14,7 @@ Choose your platform and run ZITADEL with the most minimal configuration possibl ## Prerequisites - For test environments, ZITADEL does not need many resources, 1 CPU and 512MB memory are more than enough. (With more CPU, the password hashing might be faster) -- A CockroachDB or Postgresql as only needed storage. Make sure to read our [Production Guide](./self-hosting/manage/production#prefer-cockroachdb) before you decide to use Postgresql. +- A CockroachDB or Postgresql as only needed storage. Make sure to read our [Production Guide](/docs/self-hosting/manage/production#prefer-cockroachdb) before you decide to use Postgresql. ) ## Releases diff --git a/docs/docs/self-hosting/manage/database/_postgres.mdx b/docs/docs/self-hosting/manage/database/_postgres.mdx index ace58ef32..d012ac080 100644 --- a/docs/docs/self-hosting/manage/database/_postgres.mdx +++ b/docs/docs/self-hosting/manage/database/_postgres.mdx @@ -10,7 +10,7 @@ Be aware that PostgreSQL is only [Enterprise Supported](/docs/support/software-r ::: If you want to use a PostgreSQL database instead of CockroachDB you can [overwrite the default configuration](../configure/configure.mdx). -Make sure to read our [Production Guide](./self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. +Make sure to read our [Production Guide](/docs/self-hosting/manage/production#prefer-cockroachdb) before you decide to use it. Currently versions >= 14 are supported. diff --git a/docs/docs/self-hosting/manage/productionchecklist.md b/docs/docs/self-hosting/manage/productionchecklist.md index e6f7d1bac..2f02361b7 100644 --- a/docs/docs/self-hosting/manage/productionchecklist.md +++ b/docs/docs/self-hosting/manage/productionchecklist.md @@ -9,13 +9,15 @@ To apply best practices to your production setup we created a step by step check - [ ] Make use of configuration management tools such as Terraform to provision all of the below - [ ] Use a secrets manager to store your confidential information -- [ ] Reduce the manual interaction with your platform to an absolute minimum +- [ ] Reduce the manual interaction with your platform to an absolute minimum + #### HA Setup + - [ ] High Availability for ZITADEL containers - [ ] Use a container orchestrator such as Kubernetes - [ ] Use serverless platform such as Knative or a hyperscaler equivalent (e.g. CloudRun from Google) - [ ] Split `zitadel init` and `zitadel setup` for fast start-up times when [scaling](/docs/self-hosting/manage/updating_scaling) ZITADEL -- [ ] High Availability for database +- [ ] High Availability for database - [ ] Follow the [Production Checklist](https://www.cockroachlabs.com/docs/stable/recommended-production-settings.html) for CockroachDB if you selfhost the database or use [CockroachDB cloud](https://www.cockroachlabs.com/docs/cockroachcloud/create-an-account.html) - [ ] Configure backups on a regular basis for the database - [ ] Test the restore scenarios before going live @@ -26,12 +28,14 @@ To apply best practices to your production setup we created a step by step check - [ ] Web Application Firewall #### Networking + - [ ] Use a Layer 7 Web Application Firewall to secure ZITADEL that supports **[HTTP/2](/docs/self-hosting/manage/http2)** - [ ] Limit the access by IP addresses if needed - - [ ] Secure the access by rate limits for specific endpoints (e.g. API vs frontend) to secure availability on high load. See the [ZITADEL Cloud rate limits](https://zitadel.com/docs/apis/ratelimits) for reference. - - [ ] Check that your firewall also filters IPv6 traffic``` + - [ ] Secure the access by rate limits for specific endpoints (e.g. API vs frontend) to secure availability on high load. See the [ZITADEL Cloud rate limits](/docs/legal/rate-limit-policy) for reference. + - [ ] Check that your firewall also filters IPv6 traffic ### ZITADEL configuration + - [ ] Configure a valid [SMTP Server](/docs/guides/manage/console/instance-settings#smtp) and test the email delivery - [ ] Add [Custom Branding](/docs/guides/manage/customize/branding) if required - [ ] Configure a valid [SMS Service](/docs/guides/manage/console/instance-settings#sms) such as Twilio if needed @@ -40,12 +44,14 @@ To apply best practices to your production setup we created a step by step check - [ ] Declare and apply zitadel configuration using the zitadel terraform [provider](https://github.com/zitadel/terraform-provider-zitadel) ### Security + - [ ] Use a FQDN and a trusted valid certificate for external [TLS](/docs/self-hosting/manage/tls_modes#http2) connections - [ ] Create service accounts for applications that interact with ZITADEL's APIs - [ ] Make use of a CDN service to decrease the load for static assets served by ZITADEL - [ ] Make use of a [security scanner](https://owasp.org/www-community/Vulnerability_Scanning_Tools) to test your application and deployment environment ### Monitoring + Use an appropriate monitoring solution to have an overview about your ZITADEL instance. In particular you may want to watch out for things like: - [ ] CPU and memory of ZITADEL and the database diff --git a/docs/docs/support/technical_advisory.mdx b/docs/docs/support/technical_advisory.mdx index c866c3293..7fba13420 100644 --- a/docs/docs/support/technical_advisory.mdx +++ b/docs/docs/support/technical_advisory.mdx @@ -28,6 +28,13 @@ We understand that these advisories may include breaking changes, and we aim to +## Subscribe to our Mailing List + +If you want to stay up to date on our technical advisories, we recommend subscribing to the mailing list. +Go to the subscription form and add your email address. + +As ZITADEL Cloud customer, you can also login to the ZITADEL Customer Portal and enable the Technical Advisory Notifications in your settings. + ## Categories ### Breaking Behaviour Change diff --git a/docs/docs/support/troubleshooting.md b/docs/docs/support/troubleshooting.md index 05e986fd4..8027152c3 100644 --- a/docs/docs/support/troubleshooting.md +++ b/docs/docs/support/troubleshooting.md @@ -37,6 +37,42 @@ If you're self hosting with a custom domain, you need to instruct ZITADEL to use You can find further instructions in our guide about [custom domains](https://zitadel.com/docs/self-hosting/manage/custom-domain). We also provide a guide on how to [configure](https://zitadel.com/docs/self-hosting/manage/configure) ZITADEL with variables from files or environment variables. +## Invalid audience + +`invalid audience (APP-Zxfako)` + +This error message refers to the audience claim (`aud`) of your token. +This claim identifies the audience, i.e. the resource server, that this token is intended for. +If a resource server does not identify itself with a value in the "aud" claim when this claim is present, then the must be rejected (see [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) for more details). + +You might encounter this error message from ZITADEL, typically when you authenticated with a client in one project and trying to access an application in another project. +You need add a specific [reserved scope](http://localhost:3000/docs/apis/openidoauth/scopes#reserved-scopes) to add the projectID to the audience of the access token. + +The two scenarios should help you troubleshoot this issue: + +### Frontend to Backend + +You have one project for your frontend application and one project for your backend application. +End-users authenticate to an application in your frontend project. +The frontend then sends requests to the backend, validates the token with ZITADEL's introspection endpoint, and returns a payload to the frontend. +The backend returns the error `invalid audience (APP-Zxfako)`. + +You must add the scope `urn:zitadel:iam:org:project:id:{projectId}:aud` to the auth request that is send from the front end. +Replace `projectId` with the projectId of your backend. + +### Accessing ZITADEL's APIs + +You have a project for a frontend application. +The application should also access the API of your ZITADEL, for example to pull a list of all users and display them on a user page. +End-users authenticate to the application in the frontend project, but when calling the management API you get the error `invalid audience (APP-Zxfako)`. + +You must add the scope `urn:zitadel:iam:org:project:id:zitadel:aud` to the auth request that is send from the front end. + +When accessing your ZITADEL instance's APIs they act as a resource server. +You can check the Console or via API and see that when you open your default organization there exists a project "ZITADEL" that contains different applications for each API and the Console. +Like in the scenario above the access token requires to have an `aud` claim that includes the "ZITADEL" project. +Instead of `urn:zitadel:iam:org:project:id:zitadel:aud` you could also use `urn:zitadel:iam:org:project:id:{projectId}:aud`, where `projectId` is the projectId of the Project "ZITADEL". + ## WebFinger requirement for Tailscale The WebFinger requirement and setup is a step a user has to take outside of their IdP set-up. WebFinger is a protocol which supports the ability for OIDC issuer discovery, and we use it to prove that the user has administrative control over the domain and to retrieve the issuer. This is a requirement we have in place for all users, regardless of their IdP, who use custom OIDC with Tailscale. diff --git a/docs/sidebars.js b/docs/sidebars.js index 3615dee28..cace2f404 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -150,6 +150,23 @@ module.exports = { "guides/integrate/logout", ], }, + { + type: "category", + label: "Authenticate service users", + link: { + type: "generated-index", + title: "Authenticate Service Users", + slug: "/guides/integrate/serviceusers", + description: + "How to authenticate service users for machine-to-machine (M2M) communication between services. You also need to authenticate service users to access ZITADEL's APIs.", + }, + collapsed: true, + items: [ + "guides/integrate/private-key-jwt", + "guides/integrate/client-credentials", + "guides/integrate/pat", + ], + }, { type: "category", label: "Configure identity providers", @@ -179,21 +196,9 @@ module.exports = { collapsed: true, items: [ { - type: "category", - label: "Authenticate service users", - link: { - type: "generated-index", - title: "Authenticate Service Users", - slug: "/guides/integrate/serviceusers", - description: - "How to authenticate service users", - }, - collapsed: true, - items: [ - "guides/integrate/private-key-jwt", - "guides/integrate/client-credentials", - "guides/integrate/pat", - ], + type: 'link', + label: 'Authenticate service users', + href: '/guides/integrate/serviceusers', }, "guides/integrate/access-zitadel-apis", "guides/integrate/access-zitadel-system-api", @@ -587,10 +592,17 @@ module.exports = { type: "category", label: "Policies", collapsed: false, + link: { + type: "generated-index", + title: "Policies", + slug: "/legal/policies", + description: "Policies and guidelines in addition to our terms of services.", + }, items: [ "legal/privacy-policy", "legal/acceptable-use-policy", "legal/rate-limit-policy", + "legal/policies/account-lockout-policy", "legal/vulnerability-disclosure-policy", ], }, diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index 544f43cbb..0e88b075a 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -72,7 +72,7 @@ const features = [ description="" /> > 2]; - base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; - base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; - base64 += chars[bytes[i + 2] & 63]; - } - - if ((len % 3) === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - - return base64; -} - -function decode(base64) { - let bufferLength = base64.length * 0.75, - len = base64.length, i, p = 0, - encoded1, encoded2, encoded3, encoded4; - - if (base64[base64.length - 1] === "=") { - bufferLength--; - if (base64[base64.length - 2] === "=") { - bufferLength--; - } - } - - let arraybuffer = new ArrayBuffer(bufferLength), - bytes = new Uint8Array(arraybuffer); - - for (i = 0; i < len; i += 4) { - encoded1 = lookup[base64.charCodeAt(i)]; - encoded2 = lookup[base64.charCodeAt(i + 1)]; - encoded3 = lookup[base64.charCodeAt(i + 2)]; - encoded4 = lookup[base64.charCodeAt(i + 3)]; - - bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); - bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); - bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); - } - - return arraybuffer; -} \ No newline at end of file diff --git a/internal/api/ui/login/static/resources/scripts/utils.js b/internal/api/ui/login/static/resources/scripts/utils.js new file mode 100644 index 000000000..1dc7589a4 --- /dev/null +++ b/internal/api/ui/login/static/resources/scripts/utils.js @@ -0,0 +1,63 @@ +function coerceToBase64Url(thing, name) { + // Array or ArrayBuffer to Uint8Array + if (Array.isArray(thing)) { + thing = Uint8Array.from(thing); + } + + if (thing instanceof ArrayBuffer) { + thing = new Uint8Array(thing); + } + + // Uint8Array to base64 + if (thing instanceof Uint8Array) { + var str = ""; + var len = thing.byteLength; + + for (var i = 0; i < len; i++) { + str += String.fromCharCode(thing[i]); + } + thing = window.btoa(str); + } + + if (typeof thing !== "string") { + throw new Error("could not coerce '" + name + "' to string"); + } + + // base64 to base64url + // NOTE: "=" at the end of challenge is optional, strip it off here + thing = thing.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, ""); + + return thing; +} + +function coerceToArrayBuffer(thing, name) { + if (typeof thing === "string") { + // base64url to base64 + thing = thing.replace(/-/g, "+").replace(/_/g, "/"); + + // base64 to Uint8Array + var str = window.atob(thing); + var bytes = new Uint8Array(str.length); + for (var i = 0; i < str.length; i++) { + bytes[i] = str.charCodeAt(i); + } + thing = bytes; + } + + // Array to Uint8Array + if (Array.isArray(thing)) { + thing = new Uint8Array(thing); + } + + // Uint8Array to ArrayBuffer + if (thing instanceof Uint8Array) { + thing = thing.buffer; + } + + // error if none of the above worked + if (!(thing instanceof ArrayBuffer)) { + throw new TypeError("could not coerce '" + name + "' to ArrayBuffer"); + } + + return thing; +} diff --git a/internal/api/ui/login/static/resources/scripts/webauthn.js b/internal/api/ui/login/static/resources/scripts/webauthn.js index e333c819c..e8d9c54f4 100644 --- a/internal/api/ui/login/static/resources/scripts/webauthn.js +++ b/internal/api/ui/login/static/resources/scripts/webauthn.js @@ -1,31 +1,28 @@ function checkWebauthnSupported(button, func) { - let support = document.getElementsByClassName("wa-support"); - let noSupport = document.getElementsByClassName("wa-no-support"); - if (!window.PublicKeyCredential) { - for (let item of noSupport) { - item.classList.remove('hidden'); - } - for (let item of support) { - item.classList.add('hidden'); - } - return; + let support = document.getElementsByClassName("wa-support"); + let noSupport = document.getElementsByClassName("wa-no-support"); + if (!window.PublicKeyCredential) { + for (let item of noSupport) { + item.classList.remove("hidden"); } - document.getElementById(button).addEventListener('click', func); + for (let item of support) { + item.classList.add("hidden"); + } + return; + } + document.getElementById(button).addEventListener("click", func); } function webauthnError(error) { - let err = document.getElementById('wa-error'); - err.getElementsByClassName('cause')[0].innerText = error.message; - err.classList.remove('hidden'); + let err = document.getElementById("wa-error"); + err.getElementsByClassName("cause")[0].innerText = error.message; + err.classList.remove("hidden"); } -function bufferDecode(value) { - return decode(value); +function bufferDecode(value, name) { + return coerceToArrayBuffer(value, name); } -function bufferEncode(value) { - return encode(value) - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); +function bufferEncode(value, name) { + return coerceToBase64Url(value, name); } diff --git a/internal/api/ui/login/static/resources/scripts/webauthn_login.js b/internal/api/ui/login/static/resources/scripts/webauthn_login.js index 3b0542799..4265d76e6 100644 --- a/internal/api/ui/login/static/resources/scripts/webauthn_login.js +++ b/internal/api/ui/login/static/resources/scripts/webauthn_login.js @@ -1,41 +1,54 @@ -document.addEventListener('DOMContentLoaded', checkWebauthnSupported('btn-login', login)); +document.addEventListener( + "DOMContentLoaded", + checkWebauthnSupported("btn-login", login) +); function login() { - document.getElementById('wa-error').classList.add('hidden'); + document.getElementById("wa-error").classList.add("hidden"); - let makeAssertionOptions = JSON.parse(atob(document.getElementsByName('credentialAssertionData')[0].value)); - makeAssertionOptions.publicKey.challenge = bufferDecode(makeAssertionOptions.publicKey.challenge); - makeAssertionOptions.publicKey.allowCredentials.forEach(function (listItem) { - listItem.id = bufferDecode(listItem.id) - }); - navigator.credentials.get({ - publicKey: makeAssertionOptions.publicKey - }).then(function (credential) { - verifyAssertion(credential); - }).catch(function (err) { - webauthnError(err); + let makeAssertionOptions = JSON.parse( + atob(document.getElementsByName("credentialAssertionData")[0].value) + ); + makeAssertionOptions.publicKey.challenge = bufferDecode( + makeAssertionOptions.publicKey.challenge, + "publicKey.challenge" + ); + makeAssertionOptions.publicKey.allowCredentials.forEach(function (listItem) { + listItem.id = bufferDecode(listItem.id, "publicKey.allowCredentials.id"); + }); + navigator.credentials + .get({ + publicKey: makeAssertionOptions.publicKey, + }) + .then(function (credential) { + verifyAssertion(credential); + }) + .catch(function (err) { + webauthnError(err); }); } function verifyAssertion(assertedCredential) { - let authData = new Uint8Array(assertedCredential.response.authenticatorData); - let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); - let rawId = new Uint8Array(assertedCredential.rawId); - let sig = new Uint8Array(assertedCredential.response.signature); - let userHandle = new Uint8Array(assertedCredential.response.userHandle); + let authData = new Uint8Array(assertedCredential.response.authenticatorData); + let clientDataJSON = new Uint8Array( + assertedCredential.response.clientDataJSON + ); + let rawId = new Uint8Array(assertedCredential.rawId); + let sig = new Uint8Array(assertedCredential.response.signature); + let userHandle = new Uint8Array(assertedCredential.response.userHandle); - let data = JSON.stringify({ - id: assertedCredential.id, - rawId: bufferEncode(rawId), - type: assertedCredential.type, - response: { - authenticatorData: bufferEncode(authData), - clientDataJSON: bufferEncode(clientDataJSON), - signature: bufferEncode(sig), - userHandle: bufferEncode(userHandle), - }, - }) + let data = JSON.stringify({ + id: assertedCredential.id, + rawId: bufferEncode(rawId), + type: assertedCredential.type, + response: { + authenticatorData: bufferEncode(authData), + clientDataJSON: bufferEncode(clientDataJSON), + signature: bufferEncode(sig), + userHandle: bufferEncode(userHandle), + }, + }); - document.getElementsByName('credentialData')[0].value = btoa(data); - document.getElementsByTagName('form')[0].submit(); -} \ No newline at end of file + document.getElementsByName("credentialData")[0].value = btoa(data); + document.getElementsByTagName("form")[0].submit(); +} diff --git a/internal/api/ui/login/static/resources/scripts/webauthn_register.js b/internal/api/ui/login/static/resources/scripts/webauthn_register.js index 153f6f1ab..7ce875905 100644 --- a/internal/api/ui/login/static/resources/scripts/webauthn_register.js +++ b/internal/api/ui/login/static/resources/scripts/webauthn_register.js @@ -1,42 +1,61 @@ -document.addEventListener('DOMContentLoaded', checkWebauthnSupported('btn-register', registerCredential)); +document.addEventListener( + "DOMContentLoaded", + checkWebauthnSupported("btn-register", registerCredential) +); function registerCredential() { - document.getElementById('wa-error').classList.add('hidden'); + document.getElementById("wa-error").classList.add("hidden"); - let opt = JSON.parse(atob(document.getElementsByName('credentialCreationData')[0].value)); - opt.publicKey.challenge = bufferDecode(opt.publicKey.challenge); - opt.publicKey.user.id = bufferDecode(opt.publicKey.user.id); - if (opt.publicKey.excludeCredentials) { - for (let i = 0; i < opt.publicKey.excludeCredentials.length; i++) { - if (opt.publicKey.excludeCredentials[i].id !== null) { - opt.publicKey.excludeCredentials[i].id = bufferDecode(opt.publicKey.excludeCredentials[i].id); - } - } + let opt = JSON.parse( + atob(document.getElementsByName("credentialCreationData")[0].value) + ); + opt.publicKey.challenge = bufferDecode( + opt.publicKey.challenge, + "publicKey.challenge" + ); + opt.publicKey.user.id = bufferDecode( + opt.publicKey.user.id, + "publicKey.user.id" + ); + if (opt.publicKey.excludeCredentials) { + for (let i = 0; i < opt.publicKey.excludeCredentials.length; i++) { + if (opt.publicKey.excludeCredentials[i].id !== null) { + opt.publicKey.excludeCredentials[i].id = bufferDecode( + opt.publicKey.excludeCredentials[i].id, + "publicKey.excludeCredentials" + ); + } } - navigator.credentials.create({ - publicKey: opt.publicKey - }).then(function (credential) { - createCredential(credential); - }).catch(function (err) { - webauthnError(err); + } + navigator.credentials + .create({ + publicKey: opt.publicKey, + }) + .then(function (credential) { + createCredential(credential); + }) + .catch(function (err) { + webauthnError(err); }); } function createCredential(newCredential) { - let attestationObject = new Uint8Array(newCredential.response.attestationObject); - let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); - let rawId = new Uint8Array(newCredential.rawId); + let attestationObject = new Uint8Array( + newCredential.response.attestationObject + ); + let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); + let rawId = new Uint8Array(newCredential.rawId); - let data = JSON.stringify({ - id: newCredential.id, - rawId: bufferEncode(rawId), - type: newCredential.type, - response: { - attestationObject: bufferEncode(attestationObject), - clientDataJSON: bufferEncode(clientDataJSON), - }, - }); + let data = JSON.stringify({ + id: newCredential.id, + rawId: bufferEncode(rawId), + type: newCredential.type, + response: { + attestationObject: bufferEncode(attestationObject), + clientDataJSON: bufferEncode(clientDataJSON), + }, + }); - document.getElementsByName('credentialData')[0].value = btoa(data); - document.getElementsByTagName('form')[0].submit(); -} \ No newline at end of file + document.getElementsByName("credentialData")[0].value = btoa(data); + document.getElementsByTagName("form")[0].submit(); +} diff --git a/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css b/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css index acbae860e..6f7a3e972 100644 --- a/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css +++ b/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css @@ -218,7 +218,7 @@ body.waiting * { footer { width: 100%; box-sizing: border-box; - background: rgba(0, 0, 0, 0.1254901961); + background: #00000020; min-height: 50px; display: flex; align-items: center; @@ -759,7 +759,7 @@ i { letter-spacing: 0.05em; font-size: 12px; white-space: nowrap; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843); + box-shadow: 0 0 3px #0000001a; width: fit-content; line-height: 1rem; } @@ -1211,7 +1211,7 @@ i { footer { width: 100%; box-sizing: border-box; - background: rgba(0, 0, 0, 0.1254901961); + background: #00000020; min-height: 50px; display: flex; align-items: center; @@ -1752,7 +1752,7 @@ i { letter-spacing: 0.05em; font-size: 12px; white-space: nowrap; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.1019607843); + box-shadow: 0 0 3px #0000001a; width: fit-content; line-height: 1rem; } diff --git a/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css.map b/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css.map index c1bd90d0c..0fa129cf1 100644 --- a/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css.map +++ b/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/styles/vars.scss","../../scss/main.scss","../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/mfa/mfa.scss","../../scss/styles/mfa/mfa_base.scss","../../scss/styles/register/register.scss","../../scss/styles/animations.scss","../../scss/styles/typography/typography.scss","../../scss/styles/core/core.scss","../../scss/styles/header/header_theme.scss","../../scss/styles/button/button_theme.scss","../../scss/styles/elevation/elevation.scss","../../scss/styles/input/input_theme.scss","../../scss/styles/radio/radio_theme.scss","../../scss/styles/checkbox/checkbox_theme.scss","../../scss/styles/label/label_theme.scss","../../scss/styles/footer/footer_theme.scss","../../scss/styles/a/a_theme.scss","../../scss/styles/error/error_theme.scss","../../scss/styles/qrcode/qrcode_theme.scss","../../scss/styles/container/container_theme.scss","../../scss/styles/account_selection/account_selection_theme.scss","../../scss/styles/avatar/avatar_theme.scss","../../scss/styles/select/select_theme.scss","../../scss/styles/list/list_theme.scss","../../scss/styles/identity_provider/identity_provider_theme.scss","../../scss/styles/success_label/success_label_theme.scss","../../scss/styles/mfa/mfa_theme.scss"],"names":[],"mappings":";AAAA;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;AAAA;AAAA;EAIA;EAEA;EACA;EACA;EACA;AAAA;AAAA;EAGA;EACA;EACA;EACA;AAAA;AAAA;EAIA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;AAAA;AAAA;EAIA;EAEA;EACA;EACA;EACA;AAAA;AAAA;EAGA;EACA;EACA;EACA;AAAA;AAAA;AAIA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AC/NF;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AChBJ;EACE;EACA;EACA;EACA,YAPc;EAQd;EACA;EACA,SATe;;AAWf;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EAnBF;IAoBI;IACA;IACA;IACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;AAIJ;EACE;EACA;EACA;;;AC3CN;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;ACjBJ;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;;AAEA;EACE;;AAGF;EACE;;;AD3CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADvCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;AAAA;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADhBR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AEvBJ;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;ADGtB;EACE;;;AEEJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBJ;EDqBE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA,QA9Ba;EA+Bb;EACA,SA7BsB;EA8BtB;EACA;EACA;EACA;EACA;EACA,WAzCkB;EA0ClB;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA,OArDW;EAsDX,QAtDW;EAuDX;EACA;;AAGF;EACE;EACA;EACA,OA7DmB;EA8DnB,QA9DmB;EA+DnB;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAOA;EACE;EACA;;AAGF;EACE;;AAKN;AAAA;AAAA;EAGE;;;AE7GJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;;ACTJ;ECOI;EACA,QAVa;EAWb;EACA;EACA;EACA;EACA,SAdc;EAed,eAboB;EAcpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAxB4B;EAyB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AChEZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;AAIR;EACI;;;ACXJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AC7HF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;;AC/DR;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;ACrBJ;ECCE;EACA;EACA;EACA,WANuB;EAOvB;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA,WA5BqB;EA6BrB;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtDN;ECCE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;;ACfA;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACE;EACA;;;ACFF;ECDE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;A9BlCR;EACE;EACA;EACA;EACA,YAPc;EAQd;EACA;EACA,SATe;;AAWf;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EAnBF;IAoBI;IACA;IACA;IACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;AAIJ;EACE;EACA;EACA;;;AC3CN;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;ACjBJ;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;;AAEA;EACE;;AAGF;EACE;;;AD3CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADvCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;AAAA;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADhBR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AEvBJ;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;ADGtB;EACE;;;AEEJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBJ;EDqBE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA,QA9Ba;EA+Bb;EACA,SA7BsB;EA8BtB;EACA;EACA;EACA;EACA;EACA,WAzCkB;EA0ClB;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA,OArDW;EAsDX,QAtDW;EAuDX;EACA;;AAGF;EACE;EACA;EACA,OA7DmB;EA8DnB,QA9DmB;EA+DnB;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAOA;EACE;EACA;;AAGF;EACE;;AAKN;AAAA;AAAA;EAGE;;;AE7GJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;;ACTJ;ECOI;EACA,QAVa;EAWb;EACA;EACA;EACA;EACA,SAdc;EAed,eAboB;EAcpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAxB4B;EAyB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AChEZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;AAIR;EACI;;;ACXJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AC7HF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;;AC/DR;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;ACrBJ;ECCE;EACA;EACA;EACA,WANuB;EAOvB;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA,WA5BqB;EA6BrB;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtDN;ECCE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;;ACfA;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACE;EACA;;;ACFF;ECDE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;APtCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AZlBJ;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AgB9HE;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAIR;EACI;;;A9BdR;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;A+BnBJ;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIN;EACI;EACA;;;ACqIA;EANE;EACA,aAlEY;EAGd;EAsEE;;;AAGF;EAXE;EACA,aAlEY;EAGd;EA2EE;;;AAGF;EAhBE;EACA,aAlEY;EAGd;EAgFE;;;AAGF;EArBE;EACA,aAlEY;EAGd;EAqFE;;;AAGF;EA1BE;EACA,aAlEY;EAGd;;;AA4FA;EA9BE;EACA,aAlEY;EAGd;;AA+FE;EACE;;;AAIJ;EAtCE;EACA,aAlEY;EAGd;;;AAwGA;EA1CE;EACA,aAlEY;EAGd;;;AA4GA;EA9CE;EACA,aAlEY;EAGd;;;AAgHA;EAlDE;EACA,aAlEY;EAGd;;;AAoHA;EAtDE;EACA,aAlEY;EAGd;EAsHI;;;AAGJ;EA3DE;EACA,aAlEY;EAGd;EA2HE;;;AAGF;EAhEE;EACA,aAlEY;EAGd;EAgIE;;;AAGF;EArEE;EACA,aAlEY;EAGd;EAqIE;;;AAGF;EA1EE;EACA,aAlEY;EAGd;EA0IE;;;ACvNF;EACE;EACA;;;AAKA;EACE;;;ACTJ;EACI;EACA;;;ACAN;AAAA;AAAA;EAGE;EACA;;AA0HF;AAAA;AAAA;EACE,OAPM;;AASR;AAAA;AAAA;EACE,OAVM;;AAYR;AAAA;AAAA;EACE,OAbM;;AAoBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;;AApIJ;AAAA;EAEE;;;AAGF;EACE;;;AAGF;AAAA;EC2GA;EDxGE;;;AAGF;EACE;;;AAqGF;EACE,OAPM;;AASR;EACE,OAVM;;AAYR;EACE,OAbM;;AAoBN;EACE;;AA/GF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;;AAiFF;EACE,OAPM;;AASR;EACE,OAVM;;AAYR;EACE,OAbM;;AAoBN;EACE;;AAMJ;EACE;;AAEF;EACE;;AAEF;EACE;;AAOA;EACE;;AAhHF;EACE;;;AAIJ;AAAA;ECsEA;;;ADjEA;ECiEA;;AD9DE;EC8DF;;ADtDE;ECsDF;;;AC9HA;AAAA;AAAA;EAGE;;;AAGF;AAAA;AAAA;EAGE;EACA;;AAEA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;AAIF;AAAA;AAAA;EACE;;;AAIJ;AAAA;AAAA;EAGE;;;AChCI;EACI;;AAGJ;EACI;;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;EACA;;AAIA;EACI;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;;ACtCd;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKA;EACE;EACA;;AAGF;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;;;AC7BF;EACE;;;ACFJ;EACE;EACA;EACA;;AAEA;EALF;IAMI;IACA;;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;;ACpBJ;EACE;;AAEA;EAEE;;;ACJF;EACE;;;ACAE;EACI;;AAGJ;EACI;;AAGJ;EACI;;;ACTJ;EACI;;;AAKJ;EACI;;;ACTV;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;ACnBR;EACE,kBAPM;;AASR;EACE,kBAVM;;AAYR;EACE,kBAbM;;AAoBN;EAME,kBALY;;;AChCd;EACI;;;ACCJ;AAAA;EACE;;AAIA;AAAA;EACE;;AAGF;AAAA;EACE;;;ACbN;EACE;EACA;;AAEA;Ed2HF;;AcvHE;EduHF;;AcnHE;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKF;EACE;;;AC7BJ;EACE;EACA;;;ACAQ;EACE;EACA;;AAKN;EACE;;AAGF;EACE;EACA;EACA","file":"zitadel.css"} +{"version":3,"sourceRoot":"","sources":["../../scss/styles/vars.scss","../../scss/main.scss","../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/mfa/mfa.scss","../../scss/styles/mfa/mfa_base.scss","../../scss/styles/register/register.scss","../../scss/styles/animations.scss","../../scss/styles/typography/typography.scss","../../scss/styles/core/core.scss","../../scss/styles/header/header_theme.scss","../../scss/styles/button/button_theme.scss","../../scss/styles/elevation/elevation.scss","../../scss/styles/input/input_theme.scss","../../scss/styles/radio/radio_theme.scss","../../scss/styles/checkbox/checkbox_theme.scss","../../scss/styles/label/label_theme.scss","../../scss/styles/footer/footer_theme.scss","../../scss/styles/a/a_theme.scss","../../scss/styles/error/error_theme.scss","../../scss/styles/qrcode/qrcode_theme.scss","../../scss/styles/container/container_theme.scss","../../scss/styles/account_selection/account_selection_theme.scss","../../scss/styles/avatar/avatar_theme.scss","../../scss/styles/select/select_theme.scss","../../scss/styles/list/list_theme.scss","../../scss/styles/identity_provider/identity_provider_theme.scss","../../scss/styles/success_label/success_label_theme.scss","../../scss/styles/mfa/mfa_theme.scss"],"names":[],"mappings":";AAAA;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;AAAA;AAAA;EAIA;EAEA;EACA;EACA;EACA;AAAA;AAAA;EAGA;EACA;EACA;EACA;AAAA;AAAA;EAIA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;AAAA;AAAA;EAIA;EAEA;EACA;EACA;EACA;AAAA;AAAA;EAGA;EACA;EACA;EACA;AAAA;AAAA;AAIA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AC/NF;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EACI;;;AChBJ;EACE;EACA;EACA;EACA,YAPc;EAQd;EACA;EACA,SATe;;AAWf;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EAnBF;IAoBI;IACA;IACA;IACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;AAIJ;EACE;EACA;EACA;;;AC3CN;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;ACjBJ;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;;AAEA;EACE;;AAGF;EACE;;;AD3CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADvCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;AAAA;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADhBR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AEvBJ;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;ADGtB;EACE;;;AEEJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBJ;EDqBE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA,QA9Ba;EA+Bb;EACA,SA7BsB;EA8BtB;EACA;EACA;EACA;EACA;EACA,WAzCkB;EA0ClB;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA,OArDW;EAsDX,QAtDW;EAuDX;EACA;;AAGF;EACE;EACA;EACA,OA7DmB;EA8DnB,QA9DmB;EA+DnB;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAOA;EACE;EACA;;AAGF;EACE;;AAKN;AAAA;AAAA;EAGE;;;AE7GJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;;ACTJ;ECOI;EACA,QAVa;EAWb;EACA;EACA;EACA;EACA,SAdc;EAed,eAboB;EAcpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAxB4B;EAyB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AChEZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;AAIR;EACI;;;ACXJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AC7HF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;;AC/DR;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;ACrBJ;ECCE;EACA;EACA;EACA,WANuB;EAOvB;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA,WA5BqB;EA6BrB;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtDN;ECCE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;;ACfA;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACE;EACA;;;ACFF;ECDE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;A9BlCR;EACE;EACA;EACA;EACA,YAPc;EAQd;EACA;EACA,SATe;;AAWf;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EAnBF;IAoBI;IACA;IACA;IACA;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;AAIJ;EACE;EACA;EACA;;;AC3CN;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;ACjBJ;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;;AAEA;EACE;;AAGF;EACE;;;AD3CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EACA;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADvCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;AAAA;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADhBR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AEvBJ;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;ADGtB;EACE;;;AEEJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBJ;EDqBE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA,QA9Ba;EA+Bb;EACA,SA7BsB;EA8BtB;EACA;EACA;EACA;EACA;EACA,WAzCkB;EA0ClB;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA,OArDW;EAsDX,QAtDW;EAuDX;EACA;;AAGF;EACE;EACA;EACA,OA7DmB;EA8DnB,QA9DmB;EA+DnB;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAOA;EACE;EACA;;AAGF;EACE;;AAKN;AAAA;AAAA;EAGE;;;AE7GJ;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;;ACTJ;ECOI;EACA,QAVa;EAWb;EACA;EACA;EACA;EACA,SAdc;EAed,eAboB;EAcpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAxB4B;EAyB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;AChEZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;AAIR;EACI;;;ACXJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AC7HF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;;AC/DR;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;ACrBJ;ECCE;EACA;EACA;EACA,WANuB;EAOvB;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA,WA5BqB;EA6BrB;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtDN;ECCE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;;ACfA;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACE;EACA;;;ACFF;ECDE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;APtCR;EACI;EACA;;ACFJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC7DJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AZlBJ;EACE,WAPwB;EAQxB;EACA;EACA,eAN4B;EAO5B;;AAEA;EAPF;IAQI,YAXuB;;;;AAe3B;EACE;EACA;EACA,QAnBqB;EAoBrB,SArBsB;EAsBtB;EACA;;AAGE;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;;;AgB9HE;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAIR;EACI;;;A9BdR;EACE;EACA;EACA,QALkB;EAMlB,SAPmB;EAQnB;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;A+BnBJ;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIN;EACI;EACA;;;ACqIA;EANE;EACA,aAlEY;EAGd;EAsEE;;;AAGF;EAXE;EACA,aAlEY;EAGd;EA2EE;;;AAGF;EAhBE;EACA,aAlEY;EAGd;EAgFE;;;AAGF;EArBE;EACA,aAlEY;EAGd;EAqFE;;;AAGF;EA1BE;EACA,aAlEY;EAGd;;;AA4FA;EA9BE;EACA,aAlEY;EAGd;;AA+FE;EACE;;;AAIJ;EAtCE;EACA,aAlEY;EAGd;;;AAwGA;EA1CE;EACA,aAlEY;EAGd;;;AA4GA;EA9CE;EACA,aAlEY;EAGd;;;AAgHA;EAlDE;EACA,aAlEY;EAGd;;;AAoHA;EAtDE;EACA,aAlEY;EAGd;EAsHI;;;AAGJ;EA3DE;EACA,aAlEY;EAGd;EA2HE;;;AAGF;EAhEE;EACA,aAlEY;EAGd;EAgIE;;;AAGF;EArEE;EACA,aAlEY;EAGd;EAqIE;;;AAGF;EA1EE;EACA,aAlEY;EAGd;EA0IE;;;ACvNF;EACE;EACA;;;AAKA;EACE;;;ACTJ;EACI;EACA;;;ACAN;AAAA;AAAA;EAGE;EACA;;AA0HF;AAAA;AAAA;EACE,OAPM;;AASR;AAAA;AAAA;EACE,OAVM;;AAYR;AAAA;AAAA;EACE,OAbM;;AAoBN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EACE;;;AApIJ;AAAA;EAEE;;;AAGF;EACE;;;AAGF;AAAA;EC2GA;EDxGE;;;AAGF;EACE;;;AAqGF;EACE,OAPM;;AASR;EACE,OAVM;;AAYR;EACE,OAbM;;AAoBN;EACE;;AA/GF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;;AAiFF;EACE,OAPM;;AASR;EACE,OAVM;;AAYR;EACE,OAbM;;AAoBN;EACE;;AAMJ;EACE;;AAEF;EACE;;AAEF;EACE;;AAOA;EACE;;AAhHF;EACE;;;AAIJ;AAAA;ECsEA;;;ADjEA;ECiEA;;AD9DE;EC8DF;;ADtDE;ECsDF;;;AC9HA;AAAA;AAAA;EAGE;;;AAGF;AAAA;AAAA;EAGE;EACA;;AAEA;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;AAGF;AAAA;AAAA;EACE;;AAIF;AAAA;AAAA;EACE;;;AAIJ;AAAA;AAAA;EAGE;;;AChCI;EACI;;AAGJ;EACI;;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;EACA;;AAIA;EACI;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;;ACtCd;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAKA;EACE;EACA;;AAGF;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;;;AC7BF;EACE;;;ACFJ;EACE;EACA;EACA;;AAEA;EALF;IAMI;IACA;;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;;ACpBJ;EACE;;AAEA;EAEE;;;ACJF;EACE;;;ACAE;EACI;;AAGJ;EACI;;AAGJ;EACI;;;ACTJ;EACI;;;AAKJ;EACI;;;ACTV;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;ACnBR;EACE,kBAPM;;AASR;EACE,kBAVM;;AAYR;EACE,kBAbM;;AAoBN;EAME,kBALY;;;AChCd;EACI;;;ACCJ;AAAA;EACE;;AAIA;AAAA;EACE;;AAGF;AAAA;EACE;;;ACbN;EACE;EACA;;AAEA;Ed2HF;;AcvHE;EduHF;;AcnHE;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKF;EACE;;;AC7BJ;EACE;EACA;;;ACAQ;EACE;EACA;;AAKN;EACE;;AAGF;EACE;EACA;EACA","file":"zitadel.css"} \ No newline at end of file diff --git a/internal/api/ui/login/static/templates/mfa_init_u2f.html b/internal/api/ui/login/static/templates/mfa_init_u2f.html index 7d6013d11..826defaa3 100644 --- a/internal/api/ui/login/static/templates/mfa_init_u2f.html +++ b/internal/api/ui/login/static/templates/mfa_init_u2f.html @@ -41,7 +41,7 @@
- + diff --git a/internal/api/ui/login/static/templates/mfa_verification_u2f.html b/internal/api/ui/login/static/templates/mfa_verification_u2f.html index 28ece3002..b020ae935 100644 --- a/internal/api/ui/login/static/templates/mfa_verification_u2f.html +++ b/internal/api/ui/login/static/templates/mfa_verification_u2f.html @@ -41,7 +41,7 @@ {{ end }} - + diff --git a/internal/api/ui/login/static/templates/passwordless.html b/internal/api/ui/login/static/templates/passwordless.html index 45e54264b..6a95b5407 100644 --- a/internal/api/ui/login/static/templates/passwordless.html +++ b/internal/api/ui/login/static/templates/passwordless.html @@ -37,7 +37,7 @@ - + diff --git a/internal/api/ui/login/static/templates/passwordless_registration.html b/internal/api/ui/login/static/templates/passwordless_registration.html index c5d6a1948..5ba814d66 100644 --- a/internal/api/ui/login/static/templates/passwordless_registration.html +++ b/internal/api/ui/login/static/templates/passwordless_registration.html @@ -45,7 +45,7 @@ - + diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 036a08aa9..0849c5bd0 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -211,6 +211,12 @@ func (wm *OIDCIDPWriteModel) Reduce() error { wm.reduceAddedEvent(e) case *idp.OIDCIDPChangedEvent: wm.reduceChangedEvent(e) + case *idp.OIDCIDPMigratedAzureADEvent: + wm.State = domain.IDPStateMigrated + case *idp.OIDCIDPMigratedGoogleEvent: + wm.State = domain.IDPStateMigrated + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved case *idpconfig.IDPConfigAddedEvent: wm.reduceIDPConfigAddedEvent(e) case *idpconfig.IDPConfigChangedEvent: @@ -397,6 +403,8 @@ func (wm *JWTIDPWriteModel) Reduce() error { wm.reduceAddedEvent(e) case *idp.JWTIDPChangedEvent: wm.reduceChangedEvent(e) + case *idp.RemovedEvent: + wm.State = domain.IDPStateRemoved case *idpconfig.IDPConfigAddedEvent: wm.reduceIDPConfigAddedEvent(e) case *idpconfig.IDPConfigChangedEvent: @@ -558,6 +566,8 @@ func (wm *AzureADIDPWriteModel) Reduce() error { switch e := event.(type) { case *idp.AzureADIDPAddedEvent: wm.reduceAddedEvent(e) + case *idp.OIDCIDPMigratedAzureADEvent: + wm.reduceAddedEvent(&e.AzureADIDPAddedEvent) case *idp.AzureADIDPChangedEvent: wm.reduceChangedEvent(e) case *idp.RemovedEvent: @@ -1195,6 +1205,8 @@ func (wm *GoogleIDPWriteModel) Reduce() error { wm.reduceAddedEvent(e) case *idp.GoogleIDPChangedEvent: wm.reduceChangedEvent(e) + case *idp.OIDCIDPMigratedGoogleEvent: + wm.reduceAddedEvent(&e.GoogleIDPAddedEvent) case *idp.RemovedEvent: wm.State = domain.IDPStateRemoved } diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 9d2533a12..ea6534970 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -97,6 +97,40 @@ func (c *Commands) UpdateInstanceGenericOIDCProvider(ctx context.Context, id str return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) MigrateInstanceGenericOIDCToAzureADProvider(ctx context.Context, id string, provider AzureADProvider) (*domain.ObjectDetails, error) { + return c.migrateInstanceGenericOIDC(ctx, id, provider) +} + +func (c *Commands) MigrateInstanceGenericOIDCToGoogleProvider(ctx context.Context, id string, provider GoogleProvider) (*domain.ObjectDetails, error) { + return c.migrateInstanceGenericOIDC(ctx, id, provider) +} + +func (c *Commands) migrateInstanceGenericOIDC(ctx context.Context, id string, provider interface{}) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewOIDCInstanceIDPWriteModel(instanceID, id) + + var validation preparation.Validation + switch p := provider.(type) { + case AzureADProvider: + validation = c.prepareMigrateInstanceOIDCToAzureADProvider(instanceAgg, writeModel, p) + case GoogleProvider: + validation = c.prepareMigrateInstanceOIDCToGoogleProvider(instanceAgg, writeModel, p) + default: + return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-s9219", "Errors.IDPConfig.NotExisting") + } + + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation) + if err != nil { + return nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddInstanceJWTProvider(ctx context.Context, provider JWTProvider) (string, *domain.ObjectDetails, error) { instanceID := authz.GetInstance(ctx).InstanceID() instanceAgg := instance.NewAggregate(instanceID) @@ -552,7 +586,7 @@ func (c *Commands) prepareUpdateInstanceOAuthProvider(a *instance.Aggregate, wri return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-D3r1s", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-D3r1s", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -646,7 +680,7 @@ func (c *Commands) prepareUpdateInstanceOIDCProvider(a *instance.Aggregate, writ return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-Dg331", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-Dg331", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -669,6 +703,91 @@ func (c *Commands) prepareUpdateInstanceOIDCProvider(a *instance.Aggregate, writ } } +func (c *Commands) prepareMigrateInstanceOIDCToAzureADProvider(a *instance.Aggregate, writeModel *InstanceOIDCIDPWriteModel, provider AzureADProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sdf3g", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Fhbr2", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Dzh3g", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-Dg29201", "Errors.IDPConfig.NotExisting") + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewOIDCIDPMigratedAzureADEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.Tenant, + provider.EmailVerified, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareMigrateInstanceOIDCToGoogleProvider(a *instance.Aggregate, writeModel *InstanceOIDCIDPWriteModel, provider GoogleProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-D3fvs", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-W2vqs", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-Dg29202", "Errors.IDPConfig.NotExisting") + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewOIDCIDPMigratedGoogleEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + func (c *Commands) prepareAddInstanceJWTProvider(a *instance.Aggregate, writeModel *InstanceJWTIDPWriteModel, provider JWTProvider) preparation.Validation { return func() (preparation.CreateCommands, error) { if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { @@ -742,7 +861,7 @@ func (c *Commands) prepareUpdateInstanceJWTProvider(a *instance.Aggregate, write return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-Bhju5", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-Bhju5", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -826,7 +945,7 @@ func (c *Commands) prepareUpdateInstanceAzureADProvider(a *instance.Aggregate, w return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-BHz3q", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-BHz3q", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -904,7 +1023,7 @@ func (c *Commands) prepareUpdateInstanceGitHubProvider(a *instance.Aggregate, wr return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-Dr1gs", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-Dr1gs", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1007,7 +1126,7 @@ func (c *Commands) prepareUpdateInstanceGitHubEnterpriseProvider(a *instance.Agg return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-GBr42", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-GBr42", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1086,7 +1205,7 @@ func (c *Commands) prepareUpdateInstanceGitLabProvider(a *instance.Aggregate, wr return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-HBReq", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-HBReq", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1175,7 +1294,7 @@ func (c *Commands) prepareUpdateInstanceGitLabSelfHostedProvider(a *instance.Agg return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-D2tg1", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-D2tg1", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1252,7 +1371,7 @@ func (c *Commands) prepareUpdateInstanceGoogleProvider(a *instance.Aggregate, wr return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-D3r1s", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-D3r1s", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1371,7 +1490,7 @@ func (c *Commands) prepareUpdateInstanceLDAPProvider(a *instance.Aggregate, writ return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-ASF3F", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-ASF3F", "Errors.IDPConfig.NotExisting") } event, err := writeModel.NewChangedEvent( ctx, @@ -1412,7 +1531,7 @@ func (c *Commands) prepareDeleteInstanceProvider(a *instance.Aggregate, id strin return nil, err } if !writeModel.State.Exists() { - return nil, caos_errs.ThrowNotFound(nil, "INST-Se3tg", "Errors.Instance.IDPConfig.NotExisting") + return nil, caos_errs.ThrowNotFound(nil, "INST-Se3tg", "Errors.IDPConfig.NotExisting") } return []eventstore.Command{instance.NewIDPRemovedEvent(ctx, &a.Aggregate, id)}, nil }, nil diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index 41c971cba..a0e51648d 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -113,6 +113,10 @@ func (wm *InstanceOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPChangedEvent) case *instance.IDPRemovedEvent: wm.OIDCIDPWriteModel.AppendEvents(&e.RemovedEvent) + case *instance.OIDCIDPMigratedAzureADEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedAzureADEvent) + case *instance.OIDCIDPMigratedGoogleEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedGoogleEvent) // old events case *instance.IDPConfigAddedEvent: @@ -141,6 +145,8 @@ func (wm *InstanceOIDCIDPWriteModel) Query() *eventstore.SearchQueryBuilder { instance.OIDCIDPAddedEventType, instance.OIDCIDPChangedEventType, instance.IDPRemovedEventType, + instance.OIDCIDPMigratedAzureADEventType, + instance.OIDCIDPMigratedGoogleEventType, ). EventData(map[string]interface{}{"id": wm.ID}). Or(). // old events @@ -305,6 +311,8 @@ func (wm *InstanceAzureADIDPWriteModel) AppendEvents(events ...eventstore.Event) wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPAddedEvent) case *instance.AzureADIDPChangedEvent: wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPChangedEvent) + case *instance.OIDCIDPMigratedAzureADEvent: + wm.AzureADIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedAzureADEvent) case *instance.IDPRemovedEvent: wm.AzureADIDPWriteModel.AppendEvents(&e.RemovedEvent) default: @@ -322,6 +330,7 @@ func (wm *InstanceAzureADIDPWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( instance.AzureADIDPAddedEventType, instance.AzureADIDPChangedEventType, + instance.OIDCIDPMigratedAzureADEventType, instance.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). @@ -655,6 +664,8 @@ func (wm *InstanceGoogleIDPWriteModel) AppendEvents(events ...eventstore.Event) wm.GoogleIDPWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *instance.GoogleIDPChangedEvent: wm.GoogleIDPWriteModel.AppendEvents(&e.GoogleIDPChangedEvent) + case *instance.OIDCIDPMigratedGoogleEvent: + wm.GoogleIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedGoogleEvent) case *instance.IDPRemovedEvent: wm.GoogleIDPWriteModel.AppendEvents(&e.RemovedEvent) } @@ -670,6 +681,7 @@ func (wm *InstanceGoogleIDPWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( instance.GoogleIDPAddedEventType, instance.GoogleIDPChangedEventType, + instance.OIDCIDPMigratedGoogleEventType, instance.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index ed2b5fcaf..9b9198fd4 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -1102,6 +1102,474 @@ func TestCommandSide_UpdateInstanceGenericOIDCIDP(t *testing.T) { } } +func TestCommandSide_MigrateInstanceGenericOIDCToAzureADProvider(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider AzureADProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: AzureADProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf3g", "")) + }, + }, + }, + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: AzureADProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Fhbr2", "")) + }, + }, + }, + { + "invalid client secret", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dzh3g", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "migrate ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + func() eventstore.Command { + event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ) + return event + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "migrate ok full", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + func() eventstore.Command { + event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ) + return event + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + Tenant: "tenant", + EmailVerified: true, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idpConfigEncryption: tt.fields.secretCrypto, + } + got, err := c.MigrateInstanceGenericOIDCToAzureADProvider(tt.args.ctx, tt.args.id, tt.args.provider) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_MigrateInstanceOIDCToGoogleIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + id string + provider GoogleProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GoogleProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-D3fvs", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: GoogleProvider{ + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-W2vqs", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "migrate ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + { + name: "migrate ok full", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "instance1", + instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "instance1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idpConfigEncryption: tt.fields.secretCrypto, + } + got, err := c.MigrateInstanceGenericOIDCToGoogleProvider(tt.args.ctx, tt.args.id, tt.args.provider) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + func TestCommandSide_AddInstanceAzureADIDP(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index e6e22c447..2fc0bc048 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -92,6 +92,39 @@ func (c *Commands) UpdateOrgGenericOIDCProvider(ctx context.Context, resourceOwn return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) MigrateOrgGenericOIDCToAzureADProvider(ctx context.Context, resourceOwner, id string, provider AzureADProvider) (*domain.ObjectDetails, error) { + return c.migrateOrgGenericOIDC(ctx, resourceOwner, id, provider) +} + +func (c *Commands) MigrateOrgGenericOIDCToGoogleProvider(ctx context.Context, resourceOwner, id string, provider GoogleProvider) (*domain.ObjectDetails, error) { + return c.migrateOrgGenericOIDC(ctx, resourceOwner, id, provider) +} + +func (c *Commands) migrateOrgGenericOIDC(ctx context.Context, resourceOwner, id string, provider interface{}) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewOIDCOrgIDPWriteModel(resourceOwner, id) + + var validation preparation.Validation + switch p := provider.(type) { + case AzureADProvider: + validation = c.prepareMigrateOrgOIDCToAzureADProvider(orgAgg, writeModel, p) + case GoogleProvider: + validation = c.prepareMigrateOrgOIDCToGoogleProvider(orgAgg, writeModel, p) + default: + return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-s9s2919", "Errors.IDPConfig.NotExisting") + } + + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation) + if err != nil { + return nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddOrgJWTProvider(ctx context.Context, resourceOwner string, provider JWTProvider) (string, *domain.ObjectDetails, error) { orgAgg := org.NewAggregate(resourceOwner) id, err := c.idGenerator.Next() @@ -647,6 +680,91 @@ func (c *Commands) prepareUpdateOrgOIDCProvider(a *org.Aggregate, writeModel *Or } } +func (c *Commands) prepareMigrateOrgOIDCToAzureADProvider(a *org.Aggregate, writeModel *OrgOIDCIDPWriteModel, provider AzureADProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sdf3g", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Fhbr2", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Dzh3g", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-Dg239201", "Errors.Instance.IDPConfig.NotExisting") + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewOIDCIDPMigratedAzureADEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.Tenant, + provider.EmailVerified, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareMigrateOrgOIDCToGoogleProvider(a *org.Aggregate, writeModel *OrgOIDCIDPWriteModel, provider GoogleProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-D3fvs", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-W2vqs", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-x09981", "Errors.Instance.IDPConfig.NotExisting") + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewOIDCIDPMigratedGoogleEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + func (c *Commands) prepareAddOrgJWTProvider(a *org.Aggregate, writeModel *OrgJWTIDPWriteModel, provider JWTProvider) preparation.Validation { return func() (preparation.CreateCommands, error) { if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go index ca8011121..5f121dbbf 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -113,6 +113,10 @@ func (wm *OrgOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) case *org.OIDCIDPChangedEvent: wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPChangedEvent) + case *org.OIDCIDPMigratedAzureADEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedAzureADEvent) + case *org.OIDCIDPMigratedGoogleEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedGoogleEvent) case *org.IDPRemovedEvent: wm.OIDCIDPWriteModel.AppendEvents(&e.RemovedEvent) @@ -142,6 +146,8 @@ func (wm *OrgOIDCIDPWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( org.OIDCIDPAddedEventType, org.OIDCIDPChangedEventType, + org.OIDCIDPMigratedAzureADEventType, + org.OIDCIDPMigratedGoogleEventType, org.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). @@ -311,6 +317,8 @@ func (wm *OrgAzureADIDPWriteModel) AppendEvents(events ...eventstore.Event) { wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPAddedEvent) case *org.AzureADIDPChangedEvent: wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPChangedEvent) + case *org.OIDCIDPMigratedAzureADEvent: + wm.AzureADIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedAzureADEvent) case *org.IDPRemovedEvent: wm.AzureADIDPWriteModel.AppendEvents(&e.RemovedEvent) default: @@ -328,6 +336,7 @@ func (wm *OrgAzureADIDPWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( org.AzureADIDPAddedEventType, org.AzureADIDPChangedEventType, + org.OIDCIDPMigratedAzureADEventType, org.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). @@ -663,6 +672,8 @@ func (wm *OrgGoogleIDPWriteModel) AppendEvents(events ...eventstore.Event) { wm.GoogleIDPWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) case *org.GoogleIDPChangedEvent: wm.GoogleIDPWriteModel.AppendEvents(&e.GoogleIDPChangedEvent) + case *org.OIDCIDPMigratedGoogleEvent: + wm.GoogleIDPWriteModel.AppendEvents(&e.OIDCIDPMigratedGoogleEvent) case *org.IDPRemovedEvent: wm.GoogleIDPWriteModel.AppendEvents(&e.RemovedEvent) default: @@ -680,6 +691,7 @@ func (wm *OrgGoogleIDPWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( org.GoogleIDPAddedEventType, org.GoogleIDPChangedEventType, + org.OIDCIDPMigratedGoogleEventType, org.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 7110cdd1d..063bb70b2 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -1119,6 +1119,474 @@ func TestCommandSide_UpdateOrgGenericOIDCIDP(t *testing.T) { } } +func TestCommandSide_MigrateOrgGenericOIDCToAzureADProvider(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider AzureADProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid name", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: AzureADProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdf3g", "")) + }, + }, + }, + { + "invalid client id", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: AzureADProvider{ + Name: "name", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Fhbr2", "")) + }, + }, + }, + { + "invalid client secret", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dzh3g", "")) + }, + }, + }, + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "ro", + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "migrate ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + []*repository.Event{ + eventFromEventPusher( + func() eventstore.Command { + event := org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ) + return event + }(), + ), + }, + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "migrate full ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + eventPusherToEvents( + org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: AzureADProvider{ + Name: "name", + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + Tenant: "tenant", + EmailVerified: true, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idpConfigEncryption: tt.fields.secretCrypto, + } + got, err := c.MigrateOrgGenericOIDCToAzureADProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.id, tt.args.provider) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestCommandSide_MigrateOrgOIDCToGoogleIDP(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + secretCrypto crypto.EncryptionAlgorithm + } + type args struct { + ctx context.Context + resourceOwner string + id string + provider GoogleProvider + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "invalid clientID", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GoogleProvider{}, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-D3fvs", "")) + }, + }, + }, + { + "invalid clientSecret", + fields{ + eventstore: eventstoreExpect(t), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + }, + }, + res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-W2vqs", "")) + }, + }, + }, + { + "not found", + fields{ + eventstore: eventstoreExpect(t, + expectFilter(), + ), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res{ + err: caos_errors.IsNotFound, + }, + }, + { + name: "migrate ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + eventPusherToEvents( + org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + )), + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + { + name: "migrate full ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + )), + ), + expectPush( + eventPusherToEvents( + org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + )), + ), + ), + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + }, + args: args{ + ctx: context.Background(), + resourceOwner: "org1", + id: "id1", + provider: GoogleProvider{ + ClientID: "clientID", + ClientSecret: "clientSecret", + Scopes: []string{"openid"}, + IDPOptions: idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ResourceOwner: "org1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Commands{ + eventstore: tt.fields.eventstore, + idpConfigEncryption: tt.fields.secretCrypto, + } + got, err := c.MigrateOrgGenericOIDCToGoogleProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.id, tt.args.provider) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + func TestCommandSide_AddOrgAzureADIDP(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore diff --git a/internal/command/session.go b/internal/command/session.go index cb652ea19..2feac20e0 100644 --- a/internal/command/session.go +++ b/internal/command/session.go @@ -16,10 +16,10 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -type SessionCheck func(ctx context.Context, cmd *SessionChecks) error +type SessionCommand func(ctx context.Context, cmd *SessionCommands) error -type SessionChecks struct { - checks []SessionCheck +type SessionCommands struct { + cmds []SessionCommand sessionWriteModel *SessionWriteModel passwordWriteModel *HumanPasswordWriteModel @@ -29,9 +29,9 @@ type SessionChecks struct { now func() time.Time } -func (c *Commands) NewSessionChecks(checks []SessionCheck, session *SessionWriteModel) *SessionChecks { - return &SessionChecks{ - checks: checks, +func (c *Commands) NewSessionCommands(cmds []SessionCommand, session *SessionWriteModel) *SessionCommands { + return &SessionCommands{ + cmds: cmds, sessionWriteModel: session, eventstore: c.eventstore, userPasswordAlg: c.userPasswordAlg, @@ -41,8 +41,8 @@ func (c *Commands) NewSessionChecks(checks []SessionCheck, session *SessionWrite } // CheckUser defines a user check to be executed for a session update -func CheckUser(id string) SessionCheck { - return func(ctx context.Context, cmd *SessionChecks) error { +func CheckUser(id string) SessionCommand { + return func(ctx context.Context, cmd *SessionCommands) error { if cmd.sessionWriteModel.UserID != "" && id != "" && cmd.sessionWriteModel.UserID != id { return caos_errs.ThrowInvalidArgument(nil, "", "user change not possible") } @@ -51,8 +51,8 @@ func CheckUser(id string) SessionCheck { } // CheckPassword defines a password check to be executed for a session update -func CheckPassword(password string) SessionCheck { - return func(ctx context.Context, cmd *SessionChecks) error { +func CheckPassword(password string) SessionCommand { + return func(ctx context.Context, cmd *SessionCommands) error { if cmd.sessionWriteModel.UserID == "" { return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Sfw3f", "Errors.User.UserIDMissing") } @@ -80,17 +80,32 @@ func CheckPassword(password string) SessionCheck { } } -// Check will execute the checks specified and return an error on the first occurrence -func (s *SessionChecks) Check(ctx context.Context) error { - for _, check := range s.checks { - if err := check(ctx, s); err != nil { +// Exec will execute the commands specified and returns an error on the first occurrence +func (s *SessionCommands) Exec(ctx context.Context) error { + for _, cmd := range s.cmds { + if err := cmd(ctx, s); err != nil { return err } } return nil } -func (s *SessionChecks) commands(ctx context.Context) (string, []eventstore.Command, error) { +func (s *SessionCommands) gethumanWriteModel(ctx context.Context) (*HumanWriteModel, error) { + if s.sessionWriteModel.UserID == "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing") + } + humanWriteModel := NewHumanWriteModel(s.sessionWriteModel.UserID, s.sessionWriteModel.ResourceOwner) + err := s.eventstore.FilterToQueryReducer(ctx, humanWriteModel) + if err != nil { + return nil, err + } + if humanWriteModel.UserState != domain.UserStateActive { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Df4b3", "Errors.ie4Ai.NotFound") + } + return humanWriteModel, nil +} + +func (s *SessionCommands) commands(ctx context.Context) (string, []eventstore.Command, error) { if len(s.sessionWriteModel.commands) == 0 { return "", nil, nil } @@ -103,7 +118,7 @@ func (s *SessionChecks) commands(ctx context.Context) (string, []eventstore.Comm return token, s.sessionWriteModel.commands, nil } -func (c *Commands) CreateSession(ctx context.Context, checks []SessionCheck, metadata map[string][]byte) (set *SessionChanged, err error) { +func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte) (set *SessionChanged, err error) { sessionID, err := c.idGenerator.Next() if err != nil { return nil, err @@ -113,12 +128,12 @@ func (c *Commands) CreateSession(ctx context.Context, checks []SessionCheck, met if err != nil { return nil, err } - cmd := c.NewSessionChecks(checks, sessionWriteModel) + cmd := c.NewSessionCommands(cmds, sessionWriteModel) cmd.sessionWriteModel.Start(ctx) return c.updateSession(ctx, cmd, metadata) } -func (c *Commands) UpdateSession(ctx context.Context, sessionID, sessionToken string, checks []SessionCheck, metadata map[string][]byte) (set *SessionChanged, err error) { +func (c *Commands) UpdateSession(ctx context.Context, sessionID, sessionToken string, cmds []SessionCommand, metadata map[string][]byte) (set *SessionChanged, err error) { sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetCtxData(ctx).OrgID) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) if err != nil { @@ -127,7 +142,7 @@ func (c *Commands) UpdateSession(ctx context.Context, sessionID, sessionToken st if err := c.sessionPermission(ctx, sessionWriteModel, sessionToken, domain.PermissionSessionWrite); err != nil { return nil, err } - cmd := c.NewSessionChecks(checks, sessionWriteModel) + cmd := c.NewSessionCommands(cmds, sessionWriteModel) return c.updateSession(ctx, cmd, metadata) } @@ -154,12 +169,12 @@ func (c *Commands) TerminateSession(ctx context.Context, sessionID, sessionToken return writeModelToObjectDetails(&sessionWriteModel.WriteModel), nil } -// updateSession execute the [SessionChecks] where new events will be created and as well as for metadata (changes) -func (c *Commands) updateSession(ctx context.Context, checks *SessionChecks, metadata map[string][]byte) (set *SessionChanged, err error) { +// updateSession execute the [SessionCommands] where new events will be created and as well as for metadata (changes) +func (c *Commands) updateSession(ctx context.Context, checks *SessionCommands, metadata map[string][]byte) (set *SessionChanged, err error) { if checks.sessionWriteModel.State == domain.SessionStateTerminated { return nil, caos_errs.ThrowPreconditionFailed(nil, "COMAND-SAjeh", "Errors.Session.Terminated") } - if err := checks.Check(ctx); err != nil { + if err := checks.Exec(ctx); err != nil { // TODO: how to handle failed checks (e.g. pw wrong) https://github.com/zitadel/zitadel/issues/5807 return nil, err } diff --git a/internal/command/session_model.go b/internal/command/session_model.go index e33169381..24edbfd2e 100644 --- a/internal/command/session_model.go +++ b/internal/command/session_model.go @@ -6,10 +6,31 @@ import ( "time" "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/session" + usr_repo "github.com/zitadel/zitadel/internal/repository/user" ) +type PasskeyChallengeModel struct { + Challenge string + AllowedCrentialIDs [][]byte + UserVerification domain.UserVerificationRequirement +} + +func (p *PasskeyChallengeModel) WebAuthNLogin(human *domain.Human, credentialAssertionData []byte) (*domain.WebAuthNLogin, error) { + if p == nil { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Ioqu5", "Errors.Session.Passkey.NoChallenge") + } + return &domain.WebAuthNLogin{ + ObjectRoot: human.ObjectRoot, + CredentialAssertionData: credentialAssertionData, + Challenge: p.Challenge, + AllowedCredentialIDs: p.AllowedCrentialIDs, + UserVerification: p.UserVerification, + }, nil +} + type SessionWriteModel struct { eventstore.WriteModel @@ -17,9 +38,12 @@ type SessionWriteModel struct { UserID string UserCheckedAt time.Time PasswordCheckedAt time.Time + PasskeyCheckedAt time.Time Metadata map[string][]byte State domain.SessionState + PasskeyChallenge *PasskeyChallengeModel + commands []eventstore.Command aggregate *eventstore.Aggregate } @@ -44,6 +68,10 @@ func (wm *SessionWriteModel) Reduce() error { wm.reduceUserChecked(e) case *session.PasswordCheckedEvent: wm.reducePasswordChecked(e) + case *session.PasskeyChallengedEvent: + wm.reducePasskeyChallenged(e) + case *session.PasskeyCheckedEvent: + wm.reducePasskeyChecked(e) case *session.TokenSetEvent: wm.reduceTokenSet(e) case *session.TerminateEvent: @@ -62,6 +90,8 @@ func (wm *SessionWriteModel) Query() *eventstore.SearchQueryBuilder { session.AddedType, session.UserCheckedType, session.PasswordCheckedType, + session.PasskeyChallengedType, + session.PasskeyCheckedType, session.TokenSetType, session.MetadataSetType, session.TerminateType, @@ -87,6 +117,19 @@ func (wm *SessionWriteModel) reducePasswordChecked(e *session.PasswordCheckedEve wm.PasswordCheckedAt = e.CheckedAt } +func (wm *SessionWriteModel) reducePasskeyChallenged(e *session.PasskeyChallengedEvent) { + wm.PasskeyChallenge = &PasskeyChallengeModel{ + Challenge: e.Challenge, + AllowedCrentialIDs: e.AllowedCrentialIDs, + UserVerification: e.UserVerification, + } +} + +func (wm *SessionWriteModel) reducePasskeyChecked(e *session.PasskeyCheckedEvent) { + wm.PasskeyChallenge = nil + wm.PasskeyCheckedAt = e.CheckedAt +} + func (wm *SessionWriteModel) reduceTokenSet(e *session.TokenSetEvent) { wm.TokenID = e.TokenID } @@ -110,6 +153,17 @@ func (wm *SessionWriteModel) PasswordChecked(ctx context.Context, checkedAt time wm.commands = append(wm.commands, session.NewPasswordCheckedEvent(ctx, wm.aggregate, checkedAt)) } +func (wm *SessionWriteModel) PasskeyChallenged(ctx context.Context, challenge string, allowedCrentialIDs [][]byte, userVerification domain.UserVerificationRequirement) { + wm.commands = append(wm.commands, session.NewPasskeyChallengedEvent(ctx, wm.aggregate, challenge, allowedCrentialIDs, userVerification)) +} + +func (wm *SessionWriteModel) PasskeyChecked(ctx context.Context, checkedAt time.Time, tokenID string, signCount uint32) { + wm.commands = append(wm.commands, + session.NewPasskeyCheckedEvent(ctx, wm.aggregate, checkedAt), + usr_repo.NewHumanPasswordlessSignCountChangedEvent(ctx, wm.aggregate, tokenID, signCount), + ) +} + func (wm *SessionWriteModel) SetToken(ctx context.Context, tokenID string) { wm.commands = append(wm.commands, session.NewTokenSetEvent(ctx, wm.aggregate, tokenID)) } diff --git a/internal/command/session_passkey.go b/internal/command/session_passkey.go new file mode 100644 index 000000000..d4664ca2f --- /dev/null +++ b/internal/command/session_passkey.go @@ -0,0 +1,84 @@ +package command + +import ( + "context" + "encoding/json" + + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" +) + +type humanPasskeys struct { + human *domain.Human + tokens []*domain.WebAuthNToken +} + +func (s *SessionCommands) getHumanPasskeys(ctx context.Context) (*humanPasskeys, error) { + humanWritemodel, err := s.gethumanWriteModel(ctx) + if err != nil { + return nil, err + } + tokenReadModel, err := s.getHumanPasswordlessTokenReadModel(ctx) + if err != nil { + return nil, err + } + return &humanPasskeys{ + human: writeModelToHuman(humanWritemodel), + tokens: readModelToPasswordlessTokens(tokenReadModel), + }, nil +} + +func (s *SessionCommands) getHumanPasswordlessTokenReadModel(ctx context.Context) (*HumanPasswordlessTokensReadModel, error) { + tokenReadModel := NewHumanPasswordlessTokensReadModel(s.sessionWriteModel.UserID, s.sessionWriteModel.ResourceOwner) + err := s.eventstore.FilterToQueryReducer(ctx, tokenReadModel) + if err != nil { + return nil, err + } + return tokenReadModel, nil +} + +func (c *Commands) CreatePasskeyChallenge(userVerification domain.UserVerificationRequirement, dst json.Unmarshaler) SessionCommand { + return func(ctx context.Context, cmd *SessionCommands) error { + humanPasskeys, err := cmd.getHumanPasskeys(ctx) + if err != nil { + return err + } + webAuthNLogin, err := c.webauthnConfig.BeginLogin(ctx, humanPasskeys.human, userVerification, humanPasskeys.tokens...) + if err != nil { + return err + } + if err = json.Unmarshal(webAuthNLogin.CredentialAssertionData, dst); err != nil { + return caos_errs.ThrowInternal(err, "COMMAND-Yah6A", "Errors.Internal") + } + + cmd.sessionWriteModel.PasskeyChallenged(ctx, webAuthNLogin.Challenge, webAuthNLogin.AllowedCredentialIDs, webAuthNLogin.UserVerification) + return nil + } +} + +func (c *Commands) CheckPasskey(credentialAssertionData json.Marshaler) SessionCommand { + return func(ctx context.Context, cmd *SessionCommands) error { + credentialAssertionData, err := json.Marshal(credentialAssertionData) + if err != nil { + return caos_errs.ThrowInvalidArgument(err, "COMMAND-ohG2o", "todo") + } + humanPasskeys, err := cmd.getHumanPasskeys(ctx) + if err != nil { + return err + } + webAuthN, err := cmd.sessionWriteModel.PasskeyChallenge.WebAuthNLogin(humanPasskeys.human, credentialAssertionData) + if err != nil { + return err + } + keyID, signCount, err := c.webauthnConfig.FinishLogin(ctx, humanPasskeys.human, webAuthN, credentialAssertionData, humanPasskeys.tokens...) + if err != nil && keyID == nil { + return err + } + _, token := domain.GetTokenByKeyID(humanPasskeys.tokens, keyID) + if token == nil { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Aej7i", "Errors.User.WebAuthN.NotFound") + } + cmd.sessionWriteModel.PasskeyChecked(ctx, cmd.now(), token.WebAuthNTokenID, signCount) + return nil + } +} diff --git a/internal/command/session_passkeys_test.go b/internal/command/session_passkeys_test.go new file mode 100644 index 000000000..fdc324537 --- /dev/null +++ b/internal/command/session_passkeys_test.go @@ -0,0 +1,130 @@ +package command + +import ( + "context" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/repository/org" + "github.com/zitadel/zitadel/internal/repository/user" +) + +func TestSessionCommands_getHumanPasskeys(t *testing.T) { + userAggr := &user.NewAggregate("user1", "org1").Aggregate + + type fields struct { + eventstore *eventstore.Eventstore + sessionWriteModel *SessionWriteModel + } + type res struct { + want *humanPasskeys + err error + } + tests := []struct { + name string + fields fields + res res + }{ + { + name: "missing UID", + fields: fields{ + eventstore: &eventstore.Eventstore{}, + sessionWriteModel: &SessionWriteModel{}, + }, + res: res{ + want: nil, + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing"), + }, + }, + { + name: "passwordless filter error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAggr, + "", "", "", "", "", language.Georgian, + domain.GenderDiverse, "", true, + ), + ), + ), + expectFilterError(io.ErrClosedPipe), + ), + sessionWriteModel: &SessionWriteModel{ + UserID: "user1", + }, + }, + res: res{ + want: nil, + err: io.ErrClosedPipe, + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAggr, + "", "", "", "", "", language.Georgian, + domain.GenderDiverse, "", true, + ), + ), + ), + expectFilter(eventFromEventPusher( + user.NewHumanWebAuthNAddedEvent(eventstore.NewBaseEventForPush( + context.Background(), &org.NewAggregate("org1").Aggregate, user.HumanPasswordlessTokenAddedType, + ), "111", "challenge"), + )), + ), + sessionWriteModel: &SessionWriteModel{ + UserID: "user1", + }, + }, + res: res{ + want: &humanPasskeys{ + human: &domain.Human{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + ResourceOwner: "org1", + }, + State: domain.UserStateActive, + Profile: &domain.Profile{ + PreferredLanguage: language.Georgian, + Gender: domain.GenderDiverse, + }, + Email: &domain.Email{}, + }, + tokens: []*domain.WebAuthNToken{{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "org1", + }, + WebAuthNTokenID: "111", + State: domain.MFAStateNotReady, + Challenge: "challenge", + }}, + }, + err: nil, + }, + }, + } + for _, tt := range tests { + s := &SessionCommands{ + eventstore: tt.fields.eventstore, + sessionWriteModel: tt.fields.sessionWriteModel, + } + got, err := s.getHumanPasskeys(context.Background()) + require.ErrorIs(t, err, tt.res.err) + assert.Equal(t, tt.res.want, got) + } +} diff --git a/internal/command/session_test.go b/internal/command/session_test.go index eb080480f..b9e24b03e 100644 --- a/internal/command/session_test.go +++ b/internal/command/session_test.go @@ -2,6 +2,7 @@ package command import ( "context" + "io" "testing" "time" @@ -21,6 +22,121 @@ import ( "github.com/zitadel/zitadel/internal/repository/user" ) +func TestSessionCommands_getHumanWriteModel(t *testing.T) { + userAggr := &user.NewAggregate("user1", "org1").Aggregate + + type fields struct { + eventstore *eventstore.Eventstore + sessionWriteModel *SessionWriteModel + } + type res struct { + want *HumanWriteModel + err error + } + tests := []struct { + name string + fields fields + res res + }{ + { + name: "missing UID", + fields: fields{ + eventstore: &eventstore.Eventstore{}, + sessionWriteModel: &SessionWriteModel{}, + }, + res: res{ + want: nil, + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-eeR2e", "Errors.User.UserIDMissing"), + }, + }, + { + name: "filter error", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilterError(io.ErrClosedPipe), + ), + sessionWriteModel: &SessionWriteModel{ + UserID: "user1", + }, + }, + res: res{ + want: nil, + err: io.ErrClosedPipe, + }, + }, + { + name: "removed user", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAggr, + "", "", "", "", "", language.Georgian, + domain.GenderDiverse, "", true, + ), + ), + eventFromEventPusher( + user.NewUserRemovedEvent(context.Background(), + userAggr, + "", nil, true, + ), + ), + ), + ), + sessionWriteModel: &SessionWriteModel{ + UserID: "user1", + }, + }, + res: res{ + want: nil, + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Df4b3", "Errors.ie4Ai.NotFound"), + }, + }, + { + name: "ok", + fields: fields{ + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + userAggr, + "", "", "", "", "", language.Georgian, + domain.GenderDiverse, "", true, + ), + ), + ), + ), + sessionWriteModel: &SessionWriteModel{ + UserID: "user1", + }, + }, + res: res{ + want: &HumanWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: "user1", + ResourceOwner: "org1", + Events: []eventstore.Event{}, + }, + PreferredLanguage: language.Georgian, + Gender: domain.GenderDiverse, + UserState: domain.UserStateActive, + }, + err: nil, + }, + }, + } + for _, tt := range tests { + s := &SessionCommands{ + eventstore: tt.fields.eventstore, + sessionWriteModel: tt.fields.sessionWriteModel, + } + got, err := s.gethumanWriteModel(context.Background()) + require.ErrorIs(t, err, tt.res.err) + assert.Equal(t, tt.res.want, got) + } +} + func TestCommands_CreateSession(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore @@ -29,7 +145,7 @@ func TestCommands_CreateSession(t *testing.T) { } type args struct { ctx context.Context - checks []SessionCheck + checks []SessionCommand metadata map[string][]byte } type res struct { @@ -126,7 +242,7 @@ func TestCommands_UpdateSession(t *testing.T) { ctx context.Context sessionID string sessionToken string - checks []SessionCheck + checks []SessionCommand metadata map[string][]byte } type res struct { @@ -231,7 +347,7 @@ func TestCommands_updateSession(t *testing.T) { } type args struct { ctx context.Context - checks *SessionChecks + checks *SessionCommands metadata map[string][]byte } type res struct { @@ -251,7 +367,7 @@ func TestCommands_updateSession(t *testing.T) { }, args{ ctx: context.Background(), - checks: &SessionChecks{ + checks: &SessionCommands{ sessionWriteModel: &SessionWriteModel{State: domain.SessionStateTerminated}, }, }, @@ -266,10 +382,10 @@ func TestCommands_updateSession(t *testing.T) { }, args{ ctx: context.Background(), - checks: &SessionChecks{ + checks: &SessionCommands{ sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), - checks: []SessionCheck{ - func(ctx context.Context, cmd *SessionChecks) error { + cmds: []SessionCommand{ + func(ctx context.Context, cmd *SessionCommands) error { return caos_errs.ThrowInternal(nil, "id", "check failed") }, }, @@ -286,9 +402,9 @@ func TestCommands_updateSession(t *testing.T) { }, args{ ctx: context.Background(), - checks: &SessionChecks{ + checks: &SessionCommands{ sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), - checks: []SessionCheck{}, + cmds: []SessionCommand{}, }, }, res{ @@ -321,9 +437,9 @@ func TestCommands_updateSession(t *testing.T) { }, args{ ctx: context.Background(), - checks: &SessionChecks{ + checks: &SessionCommands{ sessionWriteModel: NewSessionWriteModel("sessionID", "org1"), - checks: []SessionCheck{ + cmds: []SessionCommand{ CheckUser("userID"), CheckPassword("password"), }, diff --git a/internal/command/user_machine_secret.go b/internal/command/user_machine_secret.go index 577e18d3b..a7a91b247 100644 --- a/internal/command/user_machine_secret.go +++ b/internal/command/user_machine_secret.go @@ -14,7 +14,6 @@ import ( ) type GenerateMachineSecret struct { - ClientID string ClientSecret string } @@ -53,7 +52,6 @@ func prepareGenerateMachineSecret(a *user.Aggregate, generator crypto.Generator, if !isUserStateExists(writeModel.UserState) { return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-x8910n", "Errors.User.NotExisting") } - set.ClientID = writeModel.UserName clientSecret, secretString, err := domain.NewMachineClientSecret(generator) if err != nil { diff --git a/internal/command/user_machine_secret_test.go b/internal/command/user_machine_secret_test.go index 3965558e7..490797b96 100644 --- a/internal/command/user_machine_secret_test.go +++ b/internal/command/user_machine_secret_test.go @@ -137,7 +137,6 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) { ResourceOwner: "org1", }, secret: &GenerateMachineSecret{ - ClientID: "user1", ClientSecret: "a", }, }, @@ -157,7 +156,6 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) { } if tt.res.err == nil { assert.Equal(t, tt.res.want, got) - assert.Equal(t, tt.args.set.ClientID, tt.res.secret.ClientID) assert.Equal(t, tt.args.set.ClientSecret, tt.res.secret.ClientSecret) } }) diff --git a/internal/domain/idp.go b/internal/domain/idp.go index f276f8eb4..d06fcdd1f 100644 --- a/internal/domain/idp.go +++ b/internal/domain/idp.go @@ -9,6 +9,7 @@ const ( IDPStateActive IDPStateInactive IDPStateRemoved + IDPStateMigrated idpStateCount ) @@ -18,7 +19,7 @@ func (s IDPState) Valid() bool { } func (s IDPState) Exists() bool { - return s != IDPStateUnspecified && s != IDPStateRemoved + return s != IDPStateUnspecified && s != IDPStateRemoved && s != IDPStateMigrated } type IDPType int32 diff --git a/internal/eventstore/repository/sql/crdb.go b/internal/eventstore/repository/sql/crdb.go index f27936de4..62cf1f92a 100644 --- a/internal/eventstore/repository/sql/crdb.go +++ b/internal/eventstore/repository/sql/crdb.go @@ -140,7 +140,7 @@ func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueCons "aggregateType", event.AggregateType, "eventType", event.Type, "instanceID", event.InstanceID, - ).WithError(err).Info("query failed") + ).WithError(err).Debug("query failed") return caos_errs.ThrowInternal(err, "SQL-SBP37", "unable to create event") } } diff --git a/internal/integration/assert.go b/internal/integration/assert.go index 336139298..e5105d8d2 100644 --- a/internal/integration/assert.go +++ b/internal/integration/assert.go @@ -35,7 +35,7 @@ func AssertDetails[D DetailsMsg](t testing.TB, exptected, actual D) { gotCD := gotDetails.GetChangeDate().AsTime() now := time.Now() - assert.WithinRange(t, gotCD, now.Add(-time.Second), now.Add(time.Second)) + assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute)) assert.Equal(t, wantDetails.GetResourceOwner(), gotDetails.GetResourceOwner()) } diff --git a/internal/integration/client.go b/internal/integration/client.go new file mode 100644 index 000000000..c21b42f67 --- /dev/null +++ b/internal/integration/client.go @@ -0,0 +1,77 @@ +package integration + +import ( + "context" + "fmt" + "time" + + "github.com/zitadel/logging" + "google.golang.org/grpc" + + "github.com/zitadel/zitadel/pkg/grpc/admin" + object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha" + session "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha" + user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +type Client struct { + CC *grpc.ClientConn + Admin admin.AdminServiceClient + UserV2 user.UserServiceClient + SessionV2 session.SessionServiceClient +} + +func newClient(cc *grpc.ClientConn) Client { + return Client{ + CC: cc, + Admin: admin.NewAdminServiceClient(cc), + UserV2: user.NewUserServiceClient(cc), + SessionV2: session.NewSessionServiceClient(cc), + } +} + +func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse { + resp, err := s.Client.UserV2.AddHumanUser(ctx, &user.AddHumanUserRequest{ + Organisation: &object.Organisation{ + Org: &object.Organisation_OrgId{ + OrgId: s.Organisation.ID, + }, + }, + Profile: &user.SetHumanProfile{ + FirstName: "Mickey", + LastName: "Mouse", + }, + Email: &user.SetHumanEmail{ + Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()), + Verification: &user.SetHumanEmail_ReturnCode{ + ReturnCode: &user.ReturnEmailVerificationCode{}, + }, + }, + }) + logging.OnError(err).Fatal("create human user") + return resp +} + +func (s *Tester) RegisterUserPasskey(ctx context.Context, userID string) { + reg, err := s.Client.UserV2.CreatePasskeyRegistrationLink(ctx, &user.CreatePasskeyRegistrationLinkRequest{ + UserId: userID, + Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{}, + }) + logging.OnError(err).Fatal("create user passkey") + + pkr, err := s.Client.UserV2.RegisterPasskey(ctx, &user.RegisterPasskeyRequest{ + UserId: userID, + Code: reg.GetCode(), + }) + logging.OnError(err).Fatal("create user passkey") + attestationResponse, err := s.WebAuthN.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions()) + logging.OnError(err).Fatal("create user passkey") + + _, err = s.Client.UserV2.VerifyPasskeyRegistration(ctx, &user.VerifyPasskeyRegistrationRequest{ + UserId: userID, + PasskeyId: pkr.GetPasskeyId(), + PublicKeyCredential: attestationResponse, + PasskeyName: "nice name", + }) + logging.OnError(err).Fatal("create user passkey") +} diff --git a/internal/integration/integration.go b/internal/integration/integration.go index 67ef9d60b..6ffec11e1 100644 --- a/internal/integration/integration.go +++ b/internal/integration/integration.go @@ -29,6 +29,7 @@ import ( caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/webauthn" "github.com/zitadel/zitadel/pkg/grpc/admin" ) @@ -68,8 +69,9 @@ type Tester struct { Organisation *query.Org Users map[UserType]User - GRPCClientConn *grpc.ClientConn - wg sync.WaitGroup // used for shutdown + Client Client + WebAuthN *webauthn.Client + wg sync.WaitGroup // used for shutdown } const commandLine = `start --masterkeyFromEnv` @@ -90,7 +92,7 @@ func (s *Tester) createClientConn(ctx context.Context) { logging.OnError(err).Fatal("integration tester client dial") logging.New().WithField("target", target).Info("finished dialing grpc client conn") - s.GRPCClientConn = cc + s.Client = newClient(cc) err = s.pollHealth(ctx) logging.OnError(err).Fatal("integration tester health") } @@ -99,14 +101,12 @@ func (s *Tester) createClientConn(ctx context.Context) { // TODO: remove when we make the setup blocking on all // projections completed. func (s *Tester) pollHealth(ctx context.Context) (err error) { - client := admin.NewAdminServiceClient(s.GRPCClientConn) - for { err = func(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() - _, err := client.Healthz(ctx, &admin.HealthzRequest{}) + _, err := s.Client.Admin.Healthz(ctx, &admin.HealthzRequest{}) return err }(ctx) if err == nil { @@ -182,7 +182,7 @@ func (s *Tester) WithSystemAuthorization(ctx context.Context, u UserType) contex // Done send an interrupt signal to cleanly shutdown the server. func (s *Tester) Done() { - err := s.GRPCClientConn.Close() + err := s.Client.CC.Close() logging.OnError(err).Error("integration tester client close") s.Shutdown <- os.Interrupt @@ -238,6 +238,7 @@ func NewTester(ctx context.Context) *Tester { } tester.createClientConn(ctx) tester.createSystemUser(ctx) + tester.WebAuthN = webauthn.NewClient(tester.Config.WebAuthNName, tester.Config.ExternalDomain, "https://"+tester.Host()) return tester } diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index 4e74e6f52..80fee7e47 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -347,6 +347,10 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: instance.OIDCIDPChangedEventType, Reduce: p.reduceOIDCIDPChanged, }, + { + Event: instance.OIDCIDPMigratedAzureADEventType, + Reduce: p.reduceOIDCIDPMigratedAzureAD, + }, { Event: instance.JWTIDPAddedEventType, Reduce: p.reduceJWTIDPAdded, @@ -755,6 +759,106 @@ func (p *idpTemplateProjection) reduceOIDCIDPChanged(event eventstore.Event) (*h ), nil } +func (p *idpTemplateProjection) reduceOIDCIDPMigratedAzureAD(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.OIDCIDPMigratedAzureADEvent + switch e := event.(type) { + case *org.OIDCIDPMigratedAzureADEvent: + idpEvent = e.OIDCIDPMigratedAzureADEvent + case *instance.OIDCIDPMigratedAzureADEvent: + idpEvent = e.OIDCIDPMigratedAzureADEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedAzureADEventType, instance.OIDCIDPMigratedAzureADEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeAzureAD), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(OIDCIDCol, idpEvent.ID), + handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(AzureADIDCol, idpEvent.ID), + handler.NewCol(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(AzureADClientIDCol, idpEvent.ClientID), + handler.NewCol(AzureADClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(AzureADTenantCol, idpEvent.Tenant), + handler.NewCol(AzureADIsEmailVerified, idpEvent.IsEmailVerified), + }, + crdb.WithTableSuffix(IDPTemplateAzureADSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceOIDCIDPMigratedGoogle(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.OIDCIDPMigratedGoogleEvent + switch e := event.(type) { + case *org.OIDCIDPMigratedGoogleEvent: + idpEvent = e.OIDCIDPMigratedGoogleEvent + case *instance.OIDCIDPMigratedGoogleEvent: + idpEvent = e.OIDCIDPMigratedGoogleEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedGoogleEventType, instance.OIDCIDPMigratedGoogleEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeGoogle), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(OIDCIDCol, idpEvent.ID), + handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(GoogleIDCol, idpEvent.ID), + handler.NewCol(GoogleInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(GoogleClientIDCol, idpEvent.ClientID), + handler.NewCol(GoogleClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(GoogleScopesCol, database.StringArray(idpEvent.Scopes)), + }, + crdb.WithTableSuffix(IDPTemplateGoogleSuffix), + ), + ), nil +} + func (p *idpTemplateProjection) reduceJWTIDPAdded(event eventstore.Event) (*handler.Statement, error) { var idpEvent idp.JWTIDPAddedEvent var idpOwnerType domain.IdentityProviderType diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index 64f3fa3a4..355020717 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -2686,6 +2686,278 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { }, }, }, + { + name: "instance reduceOIDCIDPMigratedAzureAD", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.OIDCIDPMigratedAzureADEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "tenant": "tenant", + "isEmailVerified": true, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.OIDCIDPMigratedAzureADEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedAzureAD, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates5 SET (change_date, sequence, name, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (id = $9) AND (instance_id = $10)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "name", + domain.IDPTypeAzureAD, + true, + true, + true, + true, + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.idp_templates5_oidc WHERE (idp_id = $1) AND (instance_id = $2)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates5_azure (idp_id, instance_id, client_id, client_secret, scopes, tenant, is_email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + "tenant", + true, + }, + }, + }, + }, + }, + }, + { + name: "org reduceOIDCIDPMigratedAzureAD", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.OIDCIDPMigratedAzureADEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "client_id": "client_id", + "client_secret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "tenant": "tenant", + "isEmailVerified": true, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.OIDCIDPMigratedAzureADEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedAzureAD, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates5 SET (change_date, sequence, name, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (id = $9) AND (instance_id = $10)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "name", + domain.IDPTypeAzureAD, + true, + true, + true, + true, + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.idp_templates5_oidc WHERE (idp_id = $1) AND (instance_id = $2)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates5_azure (idp_id, instance_id, client_id, client_secret, scopes, tenant, is_email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + "tenant", + true, + }, + }, + }, + }, + }, + }, + { + name: "instance reduceOIDCIDPMigratedGoogle", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.OIDCIDPMigratedGoogleEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.OIDCIDPMigratedGoogleEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedGoogle, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates5 SET (change_date, sequence, name, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (id = $9) AND (instance_id = $10)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "name", + domain.IDPTypeGoogle, + true, + true, + true, + true, + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.idp_templates5_oidc WHERE (idp_id = $1) AND (instance_id = $2)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates5_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "org reduceOIDCIDPMigratedGoogle", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.OIDCIDPMigratedGoogleEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "name", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.OIDCIDPMigratedGoogleEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedGoogle, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates5 SET (change_date, sequence, name, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (id = $9) AND (instance_id = $10)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "name", + domain.IDPTypeGoogle, + true, + true, + true, + true, + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.idp_templates5_oidc WHERE (idp_id = $1) AND (instance_id = $2)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates5_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/query/projection/session.go b/internal/query/projection/session.go index 9858de473..d40302a84 100644 --- a/internal/query/projection/session.go +++ b/internal/query/projection/session.go @@ -13,7 +13,7 @@ import ( ) const ( - SessionsProjectionTable = "projections.sessions" + SessionsProjectionTable = "projections.sessions1" SessionColumnID = "id" SessionColumnCreationDate = "creation_date" @@ -26,6 +26,7 @@ const ( SessionColumnUserID = "user_id" SessionColumnUserCheckedAt = "user_checked_at" SessionColumnPasswordCheckedAt = "password_checked_at" + SessionColumnPasskeyCheckedAt = "passkey_checked_at" SessionColumnMetadata = "metadata" SessionColumnTokenID = "token_id" ) @@ -51,6 +52,7 @@ func newSessionProjection(ctx context.Context, config crdb.StatementHandlerConfi crdb.NewColumn(SessionColumnUserID, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(SessionColumnUserCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnPasswordCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), + crdb.NewColumn(SessionColumnPasskeyCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnMetadata, crdb.ColumnTypeJSONB, crdb.Nullable()), crdb.NewColumn(SessionColumnTokenID, crdb.ColumnTypeText, crdb.Nullable()), }, @@ -78,6 +80,10 @@ func (p *sessionProjection) reducers() []handler.AggregateReducer { Event: session.PasswordCheckedType, Reduce: p.reducePasswordChecked, }, + { + Event: session.PasskeyCheckedType, + Reduce: p.reducePasskeyChecked, + }, { Event: session.TokenSetType, Reduce: p.reduceTokenSet, @@ -165,6 +171,26 @@ func (p *sessionProjection) reducePasswordChecked(event eventstore.Event) (*hand ), nil } +func (p *sessionProjection) reducePasskeyChecked(event eventstore.Event) (*handler.Statement, error) { + e, ok := event.(*session.PasskeyCheckedEvent) + if !ok { + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-WieM4", "reduce.wrong.event.type %s", session.PasskeyCheckedType) + } + + return crdb.NewUpdateStatement( + e, + []handler.Column{ + handler.NewCol(SessionColumnChangeDate, e.CreationDate()), + handler.NewCol(SessionColumnSequence, e.Sequence()), + handler.NewCol(SessionColumnPasskeyCheckedAt, e.CheckedAt), + }, + []handler.Condition{ + handler.NewCond(SessionColumnID, e.Aggregate().ID), + handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), + }, + ), nil +} + func (p *sessionProjection) reduceTokenSet(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.TokenSetEvent) if !ok { diff --git a/internal/query/projection/session_test.go b/internal/query/projection/session_test.go index 6cd3ae2e5..ecb96368c 100644 --- a/internal/query/projection/session_test.go +++ b/internal/query/projection/session_test.go @@ -40,7 +40,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.sessions (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.sessions1 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -76,7 +76,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", + expectedStmt: "UPDATE projections.sessions1 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -109,7 +109,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions1 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -141,7 +141,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions1 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -175,7 +175,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions1 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -207,7 +207,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.sessions1 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -234,7 +234,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.sessions1 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, diff --git a/internal/query/session.go b/internal/query/session.go index ba30c64da..2dfc622a7 100644 --- a/internal/query/session.go +++ b/internal/query/session.go @@ -32,6 +32,7 @@ type Session struct { Creator string UserFactor SessionUserFactor PasswordFactor SessionPasswordFactor + PasskeyFactor SessionPasskeyFactor Metadata map[string][]byte } @@ -46,6 +47,10 @@ type SessionPasswordFactor struct { PasswordCheckedAt time.Time } +type SessionPasskeyFactor struct { + PasskeyCheckedAt time.Time +} + type SessionsSearchQueries struct { SearchRequest Queries []SearchQuery @@ -108,6 +113,10 @@ var ( name: projection.SessionColumnPasswordCheckedAt, table: sessionsTable, } + SessionColumnPasskeyCheckedAt = Column{ + name: projection.SessionColumnPasskeyCheckedAt, + table: sessionsTable, + } SessionColumnMetadata = Column{ name: projection.SessionColumnMetadata, table: sessionsTable, @@ -198,6 +207,7 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil LoginNameNameCol.identifier(), HumanDisplayNameCol.identifier(), SessionColumnPasswordCheckedAt.identifier(), + SessionColumnPasskeyCheckedAt.identifier(), SessionColumnMetadata.identifier(), SessionColumnToken.identifier(), ).From(sessionsTable.identifier()). @@ -212,6 +222,7 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil loginName sql.NullString displayName sql.NullString passwordCheckedAt sql.NullTime + passkeyCheckedAt sql.NullTime metadata database.Map[[]byte] token sql.NullString ) @@ -229,6 +240,7 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil &loginName, &displayName, &passwordCheckedAt, + &passkeyCheckedAt, &metadata, &token, ) @@ -245,6 +257,7 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil session.UserFactor.LoginName = loginName.String session.UserFactor.DisplayName = displayName.String session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time + session.PasskeyFactor.PasskeyCheckedAt = passkeyCheckedAt.Time session.Metadata = metadata return session, token.String, nil @@ -265,6 +278,7 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui LoginNameNameCol.identifier(), HumanDisplayNameCol.identifier(), SessionColumnPasswordCheckedAt.identifier(), + SessionColumnPasskeyCheckedAt.identifier(), SessionColumnMetadata.identifier(), countColumn.identifier(), ).From(sessionsTable.identifier()). @@ -282,6 +296,7 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui loginName sql.NullString displayName sql.NullString passwordCheckedAt sql.NullTime + passkeyCheckedAt sql.NullTime metadata database.Map[[]byte] ) @@ -298,6 +313,7 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui &loginName, &displayName, &passwordCheckedAt, + &passkeyCheckedAt, &metadata, &sessions.Count, ) @@ -310,6 +326,7 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui session.UserFactor.LoginName = loginName.String session.UserFactor.DisplayName = displayName.String session.PasswordFactor.PasswordCheckedAt = passwordCheckedAt.Time + session.PasskeyFactor.PasskeyCheckedAt = passkeyCheckedAt.Time session.Metadata = metadata sessions.Sessions = append(sessions.Sessions, session) diff --git a/internal/query/sessions_test.go b/internal/query/sessions_test.go index eeff881d8..03a5b2197 100644 --- a/internal/query/sessions_test.go +++ b/internal/query/sessions_test.go @@ -17,41 +17,43 @@ import ( ) var ( - expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions.id,` + - ` projections.sessions.creation_date,` + - ` projections.sessions.change_date,` + - ` projections.sessions.sequence,` + - ` projections.sessions.state,` + - ` projections.sessions.resource_owner,` + - ` projections.sessions.creator,` + - ` projections.sessions.user_id,` + - ` projections.sessions.user_checked_at,` + + expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions1.id,` + + ` projections.sessions1.creation_date,` + + ` projections.sessions1.change_date,` + + ` projections.sessions1.sequence,` + + ` projections.sessions1.state,` + + ` projections.sessions1.resource_owner,` + + ` projections.sessions1.creator,` + + ` projections.sessions1.user_id,` + + ` projections.sessions1.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + - ` projections.sessions.password_checked_at,` + - ` projections.sessions.metadata,` + - ` projections.sessions.token_id` + - ` FROM projections.sessions` + - ` LEFT JOIN projections.login_names2 ON projections.sessions.user_id = projections.login_names2.user_id AND projections.sessions.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions.user_id = projections.users8_humans.user_id AND projections.sessions.instance_id = projections.users8_humans.instance_id` + + ` projections.sessions1.password_checked_at,` + + ` projections.sessions1.passkey_checked_at,` + + ` projections.sessions1.metadata,` + + ` projections.sessions1.token_id` + + ` FROM projections.sessions1` + + ` LEFT JOIN projections.login_names2 ON projections.sessions1.user_id = projections.login_names2.user_id AND projections.sessions1.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions1.user_id = projections.users8_humans.user_id AND projections.sessions1.instance_id = projections.users8_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions.id,` + - ` projections.sessions.creation_date,` + - ` projections.sessions.change_date,` + - ` projections.sessions.sequence,` + - ` projections.sessions.state,` + - ` projections.sessions.resource_owner,` + - ` projections.sessions.creator,` + - ` projections.sessions.user_id,` + - ` projections.sessions.user_checked_at,` + + expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions1.id,` + + ` projections.sessions1.creation_date,` + + ` projections.sessions1.change_date,` + + ` projections.sessions1.sequence,` + + ` projections.sessions1.state,` + + ` projections.sessions1.resource_owner,` + + ` projections.sessions1.creator,` + + ` projections.sessions1.user_id,` + + ` projections.sessions1.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + - ` projections.sessions.password_checked_at,` + - ` projections.sessions.metadata,` + + ` projections.sessions1.password_checked_at,` + + ` projections.sessions1.passkey_checked_at,` + + ` projections.sessions1.metadata,` + ` COUNT(*) OVER ()` + - ` FROM projections.sessions` + - ` LEFT JOIN projections.login_names2 ON projections.sessions.user_id = projections.login_names2.user_id AND projections.sessions.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions.user_id = projections.users8_humans.user_id AND projections.sessions.instance_id = projections.users8_humans.instance_id` + + ` FROM projections.sessions1` + + ` LEFT JOIN projections.login_names2 ON projections.sessions1.user_id = projections.login_names2.user_id AND projections.sessions1.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions1.user_id = projections.users8_humans.user_id AND projections.sessions1.instance_id = projections.users8_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) sessionCols = []string{ @@ -67,6 +69,7 @@ var ( "login_name", "display_name", "password_checked_at", + "passkey_checked_at", "metadata", "token", } @@ -84,6 +87,7 @@ var ( "login_name", "display_name", "password_checked_at", + "passkey_checked_at", "metadata", "count", } @@ -133,6 +137,7 @@ func Test_SessionsPrepare(t *testing.T) { "login-name", "display-name", testNow, + testNow, []byte(`{"key": "dmFsdWU="}`), }, }, @@ -160,6 +165,9 @@ func Test_SessionsPrepare(t *testing.T) { PasswordFactor: SessionPasswordFactor{ PasswordCheckedAt: testNow, }, + PasskeyFactor: SessionPasskeyFactor{ + PasskeyCheckedAt: testNow, + }, Metadata: map[string][]byte{ "key": []byte("value"), }, @@ -188,6 +196,7 @@ func Test_SessionsPrepare(t *testing.T) { "login-name", "display-name", testNow, + testNow, []byte(`{"key": "dmFsdWU="}`), }, { @@ -203,6 +212,7 @@ func Test_SessionsPrepare(t *testing.T) { "login-name2", "display-name2", testNow, + testNow, []byte(`{"key": "dmFsdWU="}`), }, }, @@ -230,6 +240,9 @@ func Test_SessionsPrepare(t *testing.T) { PasswordFactor: SessionPasswordFactor{ PasswordCheckedAt: testNow, }, + PasskeyFactor: SessionPasskeyFactor{ + PasskeyCheckedAt: testNow, + }, Metadata: map[string][]byte{ "key": []byte("value"), }, @@ -251,6 +264,9 @@ func Test_SessionsPrepare(t *testing.T) { PasswordFactor: SessionPasswordFactor{ PasswordCheckedAt: testNow, }, + PasskeyFactor: SessionPasskeyFactor{ + PasskeyCheckedAt: testNow, + }, Metadata: map[string][]byte{ "key": []byte("value"), }, @@ -332,6 +348,7 @@ func Test_SessionPrepare(t *testing.T) { "login-name", "display-name", testNow, + testNow, []byte(`{"key": "dmFsdWU="}`), "tokenID", }, @@ -354,6 +371,9 @@ func Test_SessionPrepare(t *testing.T) { PasswordFactor: SessionPasswordFactor{ PasswordCheckedAt: testNow, }, + PasskeyFactor: SessionPasskeyFactor{ + PasskeyCheckedAt: testNow, + }, Metadata: map[string][]byte{ "key": []byte("value"), }, diff --git a/internal/repository/idp/oidc.go b/internal/repository/idp/oidc.go index ea3b63c67..09437d18e 100644 --- a/internal/repository/idp/oidc.go +++ b/internal/repository/idp/oidc.go @@ -162,3 +162,93 @@ func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error return e, nil } + +type OIDCIDPMigratedAzureADEvent struct { + AzureADIDPAddedEvent +} + +func NewOIDCIDPMigratedAzureADEvent( + base *eventstore.BaseEvent, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + tenant string, + isEmailVerified bool, + options Options, +) *OIDCIDPMigratedAzureADEvent { + return &OIDCIDPMigratedAzureADEvent{ + AzureADIDPAddedEvent: AzureADIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Tenant: tenant, + IsEmailVerified: isEmailVerified, + Options: options, + }, + } +} + +func (e *OIDCIDPMigratedAzureADEvent) Data() interface{} { + return e +} + +func (e *OIDCIDPMigratedAzureADEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := AzureADIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedAzureADEvent{AzureADIDPAddedEvent: *e.(*AzureADIDPAddedEvent)}, nil +} + +type OIDCIDPMigratedGoogleEvent struct { + GoogleIDPAddedEvent +} + +func NewOIDCIDPMigratedGoogleEvent( + base *eventstore.BaseEvent, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options Options, +) *OIDCIDPMigratedGoogleEvent { + return &OIDCIDPMigratedGoogleEvent{ + GoogleIDPAddedEvent: GoogleIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Options: options, + }, + } +} + +func (e *OIDCIDPMigratedGoogleEvent) Data() interface{} { + return e +} + +func (e *OIDCIDPMigratedGoogleEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := GoogleIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedGoogleEvent{GoogleIDPAddedEvent: *e.(*GoogleIDPAddedEvent)}, nil +} diff --git a/internal/repository/instance/eventstore.go b/internal/repository/instance/eventstore.go index b6d8923ec..4161a3696 100644 --- a/internal/repository/instance/eventstore.go +++ b/internal/repository/instance/eventstore.go @@ -74,6 +74,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, OAuthIDPChangedEventType, OAuthIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, OIDCIDPAddedEventType, OIDCIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPMigratedAzureADEventType, OIDCIDPMigratedAzureADEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPMigratedGoogleEventType, OIDCIDPMigratedGoogleEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, AzureADIDPAddedEventType, AzureADIDPAddedEventMapper). diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index 612933f0e..b2d42d8ee 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -15,6 +15,8 @@ const ( OAuthIDPChangedEventType eventstore.EventType = "instance.idp.oauth.changed" OIDCIDPAddedEventType eventstore.EventType = "instance.idp.oidc.added" OIDCIDPChangedEventType eventstore.EventType = "instance.idp.oidc.changed" + OIDCIDPMigratedAzureADEventType eventstore.EventType = "instance.idp.oidc.migrated.azure" + OIDCIDPMigratedGoogleEventType eventstore.EventType = "instance.idp.oidc.migrated.google" JWTIDPAddedEventType eventstore.EventType = "instance.idp.jwt.added" JWTIDPChangedEventType eventstore.EventType = "instance.idp.jwt.changed" AzureADIDPAddedEventType eventstore.EventType = "instance.idp.azure.added" @@ -198,6 +200,90 @@ func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *e.(*idp.OIDCIDPChangedEvent)}, nil } +type OIDCIDPMigratedAzureADEvent struct { + idp.OIDCIDPMigratedAzureADEvent +} + +func NewOIDCIDPMigratedAzureADEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + tenant string, + isEmailVerified bool, + options idp.Options, +) *OIDCIDPMigratedAzureADEvent { + return &OIDCIDPMigratedAzureADEvent{ + OIDCIDPMigratedAzureADEvent: *idp.NewOIDCIDPMigratedAzureADEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPMigratedAzureADEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + tenant, + isEmailVerified, + options, + ), + } +} + +func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPMigratedAzureADEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedAzureADEvent{OIDCIDPMigratedAzureADEvent: *e.(*idp.OIDCIDPMigratedAzureADEvent)}, nil +} + +type OIDCIDPMigratedGoogleEvent struct { + idp.OIDCIDPMigratedGoogleEvent +} + +func NewOIDCIDPMigratedGoogleEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *OIDCIDPMigratedGoogleEvent { + return &OIDCIDPMigratedGoogleEvent{ + OIDCIDPMigratedGoogleEvent: *idp.NewOIDCIDPMigratedGoogleEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPMigratedAzureADEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPMigratedGoogleEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedGoogleEvent{OIDCIDPMigratedGoogleEvent: *e.(*idp.OIDCIDPMigratedGoogleEvent)}, nil +} + type JWTIDPAddedEvent struct { idp.JWTIDPAddedEvent } diff --git a/internal/repository/org/eventstore.go b/internal/repository/org/eventstore.go index 662bf77b4..16332e08f 100644 --- a/internal/repository/org/eventstore.go +++ b/internal/repository/org/eventstore.go @@ -83,6 +83,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, OAuthIDPChangedEventType, OAuthIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, OIDCIDPAddedEventType, OIDCIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPMigratedAzureADEventType, OIDCIDPMigratedAzureADEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPMigratedGoogleEventType, OIDCIDPMigratedGoogleEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, AzureADIDPAddedEventType, AzureADIDPAddedEventMapper). diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index 97b0ebffc..6c6201db1 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -15,6 +15,8 @@ const ( OAuthIDPChangedEventType eventstore.EventType = "org.idp.oauth.changed" OIDCIDPAddedEventType eventstore.EventType = "org.idp.oidc.added" OIDCIDPChangedEventType eventstore.EventType = "org.idp.oidc.changed" + OIDCIDPMigratedAzureADEventType eventstore.EventType = "org.idp.oidc.migrated.azure" + OIDCIDPMigratedGoogleEventType eventstore.EventType = "org.idp.oidc.migrated.google" JWTIDPAddedEventType eventstore.EventType = "org.idp.jwt.added" JWTIDPChangedEventType eventstore.EventType = "org.idp.jwt.changed" AzureADIDPAddedEventType eventstore.EventType = "org.idp.azure.added" @@ -198,6 +200,90 @@ func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *e.(*idp.OIDCIDPChangedEvent)}, nil } +type OIDCIDPMigratedAzureADEvent struct { + idp.OIDCIDPMigratedAzureADEvent +} + +func NewOIDCIDPMigratedAzureADEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + tenant string, + isEmailVerified bool, + options idp.Options, +) *OIDCIDPMigratedAzureADEvent { + return &OIDCIDPMigratedAzureADEvent{ + OIDCIDPMigratedAzureADEvent: *idp.NewOIDCIDPMigratedAzureADEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPMigratedAzureADEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + tenant, + isEmailVerified, + options, + ), + } +} + +func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPMigratedAzureADEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedAzureADEvent{OIDCIDPMigratedAzureADEvent: *e.(*idp.OIDCIDPMigratedAzureADEvent)}, nil +} + +type OIDCIDPMigratedGoogleEvent struct { + idp.OIDCIDPMigratedGoogleEvent +} + +func NewOIDCIDPMigratedGoogleEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *OIDCIDPMigratedGoogleEvent { + return &OIDCIDPMigratedGoogleEvent{ + OIDCIDPMigratedGoogleEvent: *idp.NewOIDCIDPMigratedGoogleEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPMigratedGoogleEventType, + ), + id, + name, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPMigratedGoogleEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPMigratedGoogleEvent{OIDCIDPMigratedGoogleEvent: *e.(*idp.OIDCIDPMigratedGoogleEvent)}, nil +} + type JWTIDPAddedEvent struct { idp.JWTIDPAddedEvent } diff --git a/internal/repository/session/eventstore.go b/internal/repository/session/eventstore.go index 3dd1875da..3253cc856 100644 --- a/internal/repository/session/eventstore.go +++ b/internal/repository/session/eventstore.go @@ -6,6 +6,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) { es.RegisterFilterEventMapper(AggregateType, AddedType, AddedEventMapper). RegisterFilterEventMapper(AggregateType, UserCheckedType, UserCheckedEventMapper). RegisterFilterEventMapper(AggregateType, PasswordCheckedType, PasswordCheckedEventMapper). + RegisterFilterEventMapper(AggregateType, PasskeyChallengedType, eventstore.GenericEventMapper[PasskeyChallengedEvent]). + RegisterFilterEventMapper(AggregateType, PasskeyCheckedType, eventstore.GenericEventMapper[PasskeyCheckedEvent]). RegisterFilterEventMapper(AggregateType, TokenSetType, TokenSetEventMapper). RegisterFilterEventMapper(AggregateType, MetadataSetType, MetadataSetEventMapper). RegisterFilterEventMapper(AggregateType, TerminateType, TerminateEventMapper) diff --git a/internal/repository/session/session.go b/internal/repository/session/session.go index 688383442..887f056e8 100644 --- a/internal/repository/session/session.go +++ b/internal/repository/session/session.go @@ -5,19 +5,22 @@ import ( "encoding/json" "time" + "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( - sessionEventPrefix = "session." - AddedType = sessionEventPrefix + "added" - UserCheckedType = sessionEventPrefix + "user.checked" - PasswordCheckedType = sessionEventPrefix + "password.checked" - TokenSetType = sessionEventPrefix + "token.set" - MetadataSetType = sessionEventPrefix + "metadata.set" - TerminateType = sessionEventPrefix + "terminated" + sessionEventPrefix = "session." + AddedType = sessionEventPrefix + "added" + UserCheckedType = sessionEventPrefix + "user.checked" + PasswordCheckedType = sessionEventPrefix + "password.checked" + PasskeyChallengedType = sessionEventPrefix + "passkey.challenged" + PasskeyCheckedType = sessionEventPrefix + "passkey.checked" + TokenSetType = sessionEventPrefix + "token.set" + MetadataSetType = sessionEventPrefix + "metadata.set" + TerminateType = sessionEventPrefix + "terminated" ) type AddedEvent struct { @@ -141,6 +144,78 @@ func PasswordCheckedEventMapper(event *repository.Event) (eventstore.Event, erro return added, nil } +type PasskeyChallengedEvent struct { + eventstore.BaseEvent `json:"-"` + + Challenge string `json:"challenge,omitempty"` + AllowedCrentialIDs [][]byte `json:"allowedCrentialIDs,omitempty"` + UserVerification domain.UserVerificationRequirement `json:"userVerification,omitempty"` +} + +func (e *PasskeyChallengedEvent) Data() interface{} { + return e +} + +func (e *PasskeyChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func (e *PasskeyChallengedEvent) SetBaseEvent(base *eventstore.BaseEvent) { + e.BaseEvent = *base +} + +func NewPasskeyChallengedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + challenge string, + allowedCrentialIDs [][]byte, + userVerification domain.UserVerificationRequirement, +) *PasskeyChallengedEvent { + return &PasskeyChallengedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + PasskeyChallengedType, + ), + Challenge: challenge, + AllowedCrentialIDs: allowedCrentialIDs, + UserVerification: userVerification, + } +} + +type PasskeyCheckedEvent struct { + eventstore.BaseEvent `json:"-"` + + CheckedAt time.Time `json:"checkedAt"` +} + +func (e *PasskeyCheckedEvent) Data() interface{} { + return e +} + +func (e *PasskeyCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func (e *PasskeyCheckedEvent) SetBaseEvent(base *eventstore.BaseEvent) { + e.BaseEvent = *base +} + +func NewPasskeyCheckedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + checkedAt time.Time, +) *PasswordCheckedEvent { + return &PasswordCheckedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + PasskeyCheckedType, + ), + CheckedAt: checkedAt, + } +} + type TokenSetEvent struct { eventstore.BaseEvent `json:"-"` diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 441b6be36..6d830b2de 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -476,6 +476,8 @@ Errors: Terminated: Session bereits beendet Token: Invalid: Session Token ist ungültig + Passkey: + NoChallenge: Sitzung ohne Passkey-Herausforderung Intent: IDPMissing: IDP ID fehlt im Request SuccessURLMissing: Success URL fehlt im Request diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 276ad07da..88763384e 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -476,6 +476,8 @@ Errors: Terminated: Session already terminated Token: Invalid: Session Token is invalid + Passkey: + NoChallenge: Session without passkey challenge Intent: IDPMissing: IDP ID is missing in the request SuccessURLMissing: Success URL is missing in the request diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index 757a73054..dbfa308c8 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -476,6 +476,8 @@ Errors: Terminated: Sesión ya terminada Token: Invalid: El identificador de sesión no es válido + Passkey: + NoChallenge: Sesión sin desafío de contraseña Intent: IDPMissing: Falta IDP en la solicitud SuccessURLMissing: Falta la URL de éxito en la solicitud diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index eb5b5471e..9d4c03ed6 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -476,6 +476,8 @@ Errors: Terminated: La session est déjà terminée Token: Invalid: Le jeton de session n'est pas valide + Passkey: + NoChallenge: Session sans défi de clé d'accès Intent: IDPMissing: IDP manquant dans la requête SuccessURLMissing: Success URL absent de la requête diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 7ce4200b7..113e99c30 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -476,6 +476,8 @@ Errors: Terminated: Sessione già terminata Token: Invalid: Il token della sessione non è valido + Passkey: + NoChallenge: Sessione senza sfida passkey Intent: IDPMissing: IDP mancante nella richiesta SuccessURLMissing: URL di successo mancante nella richiesta diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index 394880e11..fd2fd5abe 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -465,6 +465,8 @@ Errors: Terminated: セッションはすでに終了しています Token: Invalid: セッショントークンが無効です + Passkey: + NoChallenge: パスキーチャレンジなしのセッション Intent: IDPMissing: リクエストにIDP IDが含まれていません SuccessURLMissing: リクエストに成功時の URL がありません diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index 3be75368b..0c7e56ccd 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -476,6 +476,8 @@ Errors: Terminated: Sesja już zakończona Token: Invalid: Token sesji jest nieprawidłowy + Passkey: + NoChallenge: Sesja bez wyzwania klucza Intent: IDPMissing: Brak identyfikatora IDP w żądaniu SuccessURLMissing: Brak adresu URL powodzenia w żądaniu diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index eb3a76c77..3a1508e05 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -476,6 +476,8 @@ Errors: Terminated: 会话已经终止 Token: Invalid: 会话令牌是无效的 + Passkey: + NoChallenge: 没有密码挑战的会话 Intent: IDPMissing: 请求中缺少IDP ID SuccessURLMissing: 请求中缺少成功URL diff --git a/internal/webauthn/client.go b/internal/webauthn/client.go index ac7fbe766..511378fee 100644 --- a/internal/webauthn/client.go +++ b/internal/webauthn/client.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/descope/virtualwebauthn" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" ) type Client struct { @@ -25,12 +27,40 @@ func NewClient(name, domain, origin string) *Client { } } -func (c *Client) CreateAttestationResponse(options []byte) ([]byte, error) { +func (c *Client) CreateAttestationResponse(optionsPb *structpb.Struct) (*structpb.Struct, error) { + options, err := protojson.Marshal(optionsPb) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err) + } parsedAttestationOptions, err := virtualwebauthn.ParseAttestationOptions(string(options)) if err != nil { return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err) } - return []byte(virtualwebauthn.CreateAttestationResponse( + resp := new(structpb.Struct) + err = protojson.Unmarshal([]byte(virtualwebauthn.CreateAttestationResponse( c.rp, c.auth, c.credential, *parsedAttestationOptions, - )), nil + )), resp) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAttestationResponse: %w", err) + } + return resp, nil +} + +func (c *Client) CreateAssertionResponse(optionsPb *structpb.Struct) (*structpb.Struct, error) { + options, err := protojson.Marshal(optionsPb) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAssertionResponse: %w", err) + } + parsedAssertionOptions, err := virtualwebauthn.ParseAssertionOptions(string(options)) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAssertionResponse: %w", err) + } + resp := new(structpb.Struct) + err = protojson.Unmarshal([]byte(virtualwebauthn.CreateAssertionResponse( + c.rp, c.auth, c.credential, *parsedAssertionOptions, + )), resp) + if err != nil { + return nil, fmt.Errorf("webauthn.Client.CreateAssertionResponse: %w", err) + } + return resp, nil } diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 1424d7cd2..e50d3afb3 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -1342,6 +1342,24 @@ service AdminService { }; } + // Migrate an existing OIDC identity provider on the instance + rpc MigrateGenericOIDCProvider(MigrateGenericOIDCProviderRequest) returns (MigrateGenericOIDCProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_oidc/{id}/_migrate" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Identity Providers"; + summary: "Migrate Generic OIDC Identity Provider"; + description: ""; + }; + } + // Add a new JWT identity provider on the instance rpc AddJWTProvider(AddJWTProviderRequest) returns (AddJWTProviderResponse) { option (google.api.http) = { @@ -4828,6 +4846,23 @@ message UpdateGenericOIDCProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message MigrateGenericOIDCProviderRequest{ + string id = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"69629023906488334\""; + } + ]; + oneof template { + AddAzureADProviderRequest azure = 2; + AddGoogleProviderRequest google = 3; + } +} + +message MigrateGenericOIDCProviderResponse{ + zitadel.v1.ObjectDetails details = 1; +} + message AddJWTProviderRequest { string name = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index cf055e4a5..b0d5bc97f 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -6558,6 +6558,24 @@ service ManagementService { }; } + // Migrate an existing OIDC identity provider in the organization + rpc MigrateGenericOIDCProvider(MigrateGenericOIDCProviderRequest) returns (MigrateGenericOIDCProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_oidc/{id}/_migrate" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Identity Providers"; + summary: "Migrate Generic OIDC Identity Provider"; + description: ""; + }; + } + // Add a new JWT identity provider in the organization rpc AddJWTProvider(AddJWTProviderRequest) returns (AddJWTProviderResponse) { option (google.api.http) = { @@ -11526,6 +11544,23 @@ message UpdateGenericOIDCProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message MigrateGenericOIDCProviderRequest{ + string id = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"69629023906488334\""; + } + ]; + oneof template { + AddAzureADProviderRequest azure = 2; + AddGoogleProviderRequest google = 3; + } +} + +message MigrateGenericOIDCProviderResponse{ + zitadel.v1.ObjectDetails details = 1; +} + message AddJWTProviderRequest { string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string issuer = 2 [ diff --git a/proto/zitadel/session/v2alpha/challenge.proto b/proto/zitadel/session/v2alpha/challenge.proto new file mode 100644 index 000000000..498cb729b --- /dev/null +++ b/proto/zitadel/session/v2alpha/challenge.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package zitadel.session.v2alpha; + +import "google/protobuf/struct.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha;session"; + +enum ChallengeKind { + CHALLENGE_KIND_UNSPECIFIED = 0; + CHALLENGE_KIND_PASSKEY = 1; +} + +message Challenges { + message Passkey { + google.protobuf.Struct public_key_credential_request_options = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Options for Assertion Generaration (dictionary PublicKeyCredentialRequestOptions). Generated helper methods transform the field to JSON, for use in a WebauthN client. See also: https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrequestoptions" + example: "{\"publicKey\":{\"allowCredentials\":[{\"id\":\"ATmqBg-99qyOZk2zloPdJQyS2R7IkFT7v9Hoos_B_nM\",\"type\":\"public-key\"}],\"challenge\":\"GAOHYz2jE69kJMYo6Laij8yWw9-dKKgbViNhfuy0StA\",\"rpId\":\"localhost\",\"timeout\":300000,\"userVerification\":\"required\"}}" + } + ]; + } + + optional Passkey passkey = 1; +} diff --git a/proto/zitadel/session/v2alpha/session.proto b/proto/zitadel/session/v2alpha/session.proto index 279863d14..79f203086 100644 --- a/proto/zitadel/session/v2alpha/session.proto +++ b/proto/zitadel/session/v2alpha/session.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package zitadel.session.v2alpha; -import "google/api/field_behavior.proto"; import "google/protobuf/timestamp.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; @@ -45,6 +44,7 @@ message Session { message Factors { UserFactor user = 1; PasswordFactor password = 2; + PasskeyFactor passkey = 3; } message UserFactor { @@ -78,6 +78,14 @@ message PasswordFactor { ]; } +message PasskeyFactor { + google.protobuf.Timestamp verified_at = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "\"time when the passkey challenge was last checked\""; + } + ]; +} + message SearchQuery { oneof query { option (validate.required) = true; diff --git a/proto/zitadel/session/v2alpha/session_service.proto b/proto/zitadel/session/v2alpha/session_service.proto index e56462107..0ee4b05d3 100644 --- a/proto/zitadel/session/v2alpha/session_service.proto +++ b/proto/zitadel/session/v2alpha/session_service.proto @@ -5,9 +5,11 @@ package zitadel.session.v2alpha; import "zitadel/object/v2alpha/object.proto"; import "zitadel/protoc_gen_zitadel/v2/options.proto"; +import "zitadel/session/v2alpha/challenge.proto"; import "zitadel/session/v2alpha/session.proto"; import "google/api/annotations.proto"; import "google/api/field_behavior.proto"; +import "google/protobuf/struct.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; @@ -242,6 +244,7 @@ message CreateSessionRequest{ description: "\"custom key value list to be stored on the session\""; } ]; + repeated ChallengeKind challenges = 3; } message CreateSessionResponse{ @@ -257,6 +260,7 @@ message CreateSessionResponse{ description: "\"token of the session, which is required for further updates of the session or the request other resources\""; } ]; + Challenges challenges = 4; } message SetSessionRequest{ @@ -287,6 +291,7 @@ message SetSessionRequest{ description: "\"custom key value list to be stored on the session\""; } ]; + repeated ChallengeKind challenges = 5; } message SetSessionResponse{ @@ -296,6 +301,7 @@ message SetSessionResponse{ description: "\"token of the session, which is required for further updates of the session or the request other resources\""; } ]; + Challenges challenges = 3; } message DeleteSessionRequest{ @@ -330,6 +336,11 @@ message Checks { description: "\"Checks the password and updates the session on success. Requires that the user is already checked, either in the previous or the same request.\""; } ]; + optional CheckPasskey passkey = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "\"Checks the public key credential issued by the passkey client. Requires that the user is already checked and a passkey challenge to be requested, in any previous request.\""; + } + ]; } message CheckUser { @@ -363,3 +374,15 @@ message CheckPassword { } ]; } + +message CheckPasskey { + google.protobuf.Struct credential_assertion_data = 1 [ + (validate.rules).message.required = true, + (google.api.field_behavior) = REQUIRED, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "JSON representation of public key credential issued by the passkey client"; + min_length: 55; + max_length: 1048576; //1 MB + } + ]; +} diff --git a/proto/zitadel/user/v2alpha/user_service.proto b/proto/zitadel/user/v2alpha/user_service.proto index 1af446f09..9522cd96a 100644 --- a/proto/zitadel/user/v2alpha/user_service.proto +++ b/proto/zitadel/user/v2alpha/user_service.proto @@ -12,6 +12,7 @@ import "zitadel/user/v2alpha/user.proto"; import "google/api/annotations.proto"; import "google/api/field_behavior.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; @@ -429,10 +430,10 @@ message RegisterPasskeyResponse{ example: "\"fabde5c8-c13f-481d-a90b-5e59a001a076\"" } ]; - bytes public_key_credential_creation_options = 3 [ + google.protobuf.Struct public_key_credential_creation_options = 3 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "json representation of public key credential creation options used by the passkey client" - example: "\"eyJwdWJsaWNLZXkiOnsiY2hhbGxlbmdlIoplfZm4vM21qSzBPdjltN2x6VWhnclYyejFJSlVzZnpLd0Z1TytWTWtzRW1Icz0iLCJycCI6eyJuYW1lIjoiWklUQURFTCIsImlkIjoiYWNtZS1nem9lNHgueml0YWRlbC5jbG91ZCJ9LCJ1c2VyIjp7Im5hbWUiOiJ0ZXN0dXNlcjU1QGFjbWUueml0YWRlbC5jbG91ZCIsImRpc3BsYXlOYW1lIjoiVGVzdCBUZXN0IiwiaWQiOiJNVGd5TVRVMk1qWTBNakk1TXpBMk5qSTEifSwicHViS2V5Q3JlZFBhcmFtcyI6W3sidHlwZSI6InB1YmxpYy1rZXkiLCJhbGciOi03fSx7InR5cGUiOiJwdWJsaWMta2V5IiwiYWxnIjotMzV9LHsidHlwZSI6InB1YmxpYy1rZXkiLCJhbGciOi0zNn0seyJ0eXBlIjoicHVibGljLWtleSIsImFsZyI6LTI1N30seyJ0eXBlIjoicHVibGljLWtleSIsImFsZyI6LTI1OH0seyJ0eXBlIjoicHVibGljLWtleSIsImFsZyI6LTI1OX0seyJ0eXBlIjoicHVibGljLWtleSIsImFsZyI6LTM3fSx7InR5cGUiOiJwdWJsaWMta2V5IiwiYWxnIjotMzh9LHsidHlwZSI6InB1YmxpYy1rZXkiLCJhbGciOi0zOX0seyJ0eXBlIjoicHVibGljLWtleSIsImFsZyI6LTh9XSwiYXV0aGVudGljYXRvclNlbGVjdGlvbiI6eyJ1c2VyVmVyaWZpY2F0aW9uIjoiZGlzY291cmFnZWQifn2ilGltZW91dCI6NjAwMDAsImF0dGVzdGF0aW9uIjoibm9uZSJ9fQ==\"" + description: "Options for Credential Creation (dictionary PublicKeyCredentialCreationOptions). Generated helper methods transform the field to JSON, for use in a WebauthN client. See also: https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialcreationoptions" + example: "{\"publicKey\":{\"attestation\":\"none\",\"authenticatorSelection\":{\"userVerification\":\"required\"},\"challenge\":\"XaMYwWOZ5hj6pwtwJJlpcI-ExkO5TxevBMG4R8DoKQQ\",\"excludeCredentials\":[{\"id\":\"tVp1QfYhT8DkyEHVrv7blnpAo2YJzbZgZNBf7zPs6CI\",\"type\":\"public-key\"}],\"pubKeyCredParams\":[{\"alg\":-7,\"type\":\"public-key\"}],\"rp\":{\"id\":\"localhost\",\"name\":\"ZITADEL\"},\"timeout\":300000,\"user\":{\"displayName\":\"Tim Mohlmann\",\"id\":\"MjE1NTk4MDAwNDY0OTk4OTQw\",\"name\":\"tim\"}}}" } ]; } @@ -456,11 +457,12 @@ message VerifyPasskeyRegistrationRequest{ example: "\"fabde5c8-c13f-481d-a90b-5e59a001a076\""; } ]; - bytes public_key_credential = 3 [ - (validate.rules).bytes = {min_len: 55, max_len: 1048576}, + google.protobuf.Struct public_key_credential = 3 [ + (validate.rules).message.required = true, (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "JSON representation of public key credential issued by the passkey client"; + description: "PublicKeyCredential Interface. Generated helper methods populate the field from JSON created by a WebauthN client. See also: https://www.w3.org/TR/webauthn/#publickeycredential"; + example: "{\"type\":\"public-key\",\"id\":\"pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00\",\"rawId\":\"pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00\",\"response\":{\"attestationObject\":\"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0\",\"clientDataJSON\":\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ\"}}"; min_length: 55; max_length: 1048576; //1 MB } From 8c0db9a611fc393c86d8b1220a860bbe025536d3 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Thu, 8 Jun 2023 10:19:47 +0200 Subject: [PATCH 17/76] fix: empty commit --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 76d07edb6..ae5179d96 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -47,7 +47,7 @@ We will not publish this information by default to protect your privacy. ## Disclosure Process -Our security team will follow the disclosure process: +Our security team will follow the disclosure process: 1. We will acknowledge the receipt of your vulnerability report 2. Our security team will try to verify, reproduce, and determine the impact of your report From cc12e915b9e3f2b68953394aca4c8e487aef4325 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:16:39 +0200 Subject: [PATCH 18/76] fix: nil pointer on create instance add machine (#6000) * fix: nil pointer on create instance add machine * fix: instance setup with machine user pat * fix: correct logic to write pat and key from setup without configurable scope --------- Co-authored-by: Livio Spring --- cmd/setup/03.go | 51 ++++++++++++++----- cmd/setup/config.go | 1 + cmd/setup/setup.go | 1 + cmd/setup/steps.yaml | 3 ++ .../api/grpc/system/instance_converter.go | 8 ++- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/cmd/setup/03.go b/cmd/setup/03.go index 416010946..ca1e0e21c 100644 --- a/cmd/setup/03.go +++ b/cmd/setup/03.go @@ -23,10 +23,12 @@ type FirstInstance struct { DefaultLanguage language.Tag Org command.OrgSetup MachineKeyPath string + PatPath string instanceSetup command.InstanceSetup userEncryptionKey *crypto.KeyConfig smtpEncryptionKey *crypto.KeyConfig + oidcEncryptionKey *crypto.KeyConfig masterKey string db *sql.DB es *eventstore.Eventstore @@ -59,6 +61,14 @@ func (mig *FirstInstance) Execute(ctx context.Context) error { return err } + if err = verifyKey(mig.oidcEncryptionKey, keyStorage); err != nil { + return err + } + oidcEncryption, err := crypto.NewAESCrypto(mig.oidcEncryptionKey, keyStorage) + if err != nil { + return err + } + cmd, err := command.StartCommands(mig.es, mig.defaults, mig.zitadelRoles, @@ -73,13 +83,12 @@ func (mig *FirstInstance) Execute(ctx context.Context) error { nil, userAlg, nil, - nil, + oidcEncryption, nil, nil, nil, nil, ) - if err != nil { return err } @@ -101,25 +110,43 @@ func (mig *FirstInstance) Execute(ctx context.Context) error { } } - _, _, key, _, err := cmd.SetUpInstance(ctx, &mig.instanceSetup) - if key == nil { + _, token, key, _, err := cmd.SetUpInstance(ctx, &mig.instanceSetup) + if err != nil { + return err + } + if mig.instanceSetup.Org.Machine != nil && + ((mig.instanceSetup.Org.Machine.Pat != nil && token == "") || + (mig.instanceSetup.Org.Machine.MachineKey != nil && key == nil)) { return err } + if key != nil { + keyDetails, err := key.Detail() + if err != nil { + return err + } + if err := outputStdoutOrPath(mig.MachineKeyPath, string(keyDetails)); err != nil { + return err + } + } + if token != "" { + if err := outputStdoutOrPath(mig.PatPath, token); err != nil { + return err + } + } + return nil +} + +func outputStdoutOrPath(path string, content string) (err error) { f := os.Stdout - if mig.MachineKeyPath != "" { - f, err = os.OpenFile(mig.MachineKeyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if path != "" { + f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } defer f.Close() } - - keyDetails, err := key.Detail() - if err != nil { - return err - } - _, err = fmt.Fprintln(f, string(keyDetails)) + _, err = fmt.Fprintln(f, content) return err } diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 0d659e6e7..2f1bfbd6f 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -72,6 +72,7 @@ type Steps struct { type encryptionKeyConfig struct { User *crypto.KeyConfig SMTP *crypto.KeyConfig + OIDC *crypto.KeyConfig } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 3f42503c2..76f9637a2 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -75,6 +75,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.FirstInstance.instanceSetup = config.DefaultInstance steps.FirstInstance.userEncryptionKey = config.EncryptionKeys.User steps.FirstInstance.smtpEncryptionKey = config.EncryptionKeys.SMTP + steps.FirstInstance.oidcEncryptionKey = config.EncryptionKeys.OIDC steps.FirstInstance.masterKey = masterKey steps.FirstInstance.db = dbClient.DB steps.FirstInstance.es = eventstoreClient diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index e495276e5..3164ac2f4 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -1,5 +1,6 @@ FirstInstance: MachineKeyPath: + PatPath: InstanceName: ZITADEL DefaultLanguage: en Org: @@ -30,6 +31,8 @@ FirstInstance: MachineKey: ExpirationDate: Type: + Pat: + ExpirationDate: CorrectCreationDate: FailAfter: 5m diff --git a/internal/api/grpc/system/instance_converter.go b/internal/api/grpc/system/instance_converter.go index 304ab19c2..9aeea3e2c 100644 --- a/internal/api/grpc/system/instance_converter.go +++ b/internal/api/grpc/system/instance_converter.go @@ -113,13 +113,11 @@ func createInstancePbToAddMachine(req *system_pb.CreateInstanceRequest_Machine, // Scopes are currently static and can not be overwritten Scopes: []string{oidc.ScopeOpenID, z_oidc.ScopeUserMetaData, z_oidc.ScopeResourceOwner}, } - - if !defaultMachine.Pat.ExpirationDate.IsZero() { - pat.ExpirationDate = defaultMachine.Pat.ExpirationDate - } else if req.PersonalAccessToken.ExpirationDate.IsValid() { + if req.GetPersonalAccessToken().GetExpirationDate().IsValid() { pat.ExpirationDate = req.PersonalAccessToken.ExpirationDate.AsTime() + } else if defaultMachine.Pat != nil && !defaultMachine.Pat.ExpirationDate.IsZero() { + pat.ExpirationDate = defaultMachine.Pat.ExpirationDate } - machine.Pat = &pat } From 480a82b64fc44fd7b5c28d8171c3eec45f3ce560 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Thu, 15 Jun 2023 09:02:53 +0200 Subject: [PATCH 19/76] fix: update linking users if action changed values (#6024) Co-authored-by: Livio Spring --- internal/api/ui/login/external_provider_handler.go | 12 +++++++++--- internal/auth/repository/auth_request.go | 1 + .../eventsourcing/eventstore/auth_request.go | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index 2b7035c35..abd07a70e 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -292,14 +292,14 @@ func (l *Login) handleExternalUserAuthenticated( l.renderError(w, r, authReq, externalErr) return } - externalUser, externalUserChange, err := l.runPostExternalAuthenticationActions(externalUser, tokens(session), authReq, r, user, externalErr) + externalUser, externalUserChange, err := l.runPostExternalAuthenticationActions(externalUser, tokens(session), authReq, r, user, nil) if err != nil { l.renderError(w, r, authReq, err) return } // if action is done and no user linked then link or register if errors.IsNotFound(externalErr) { - l.externalUserNotExisting(w, r, authReq, provider, externalUser) + l.externalUserNotExisting(w, r, authReq, provider, externalUser, externalUserChange) return } if provider.IsAutoUpdate || len(externalUser.Metadatas) > 0 || externalUserChange { @@ -334,7 +334,7 @@ func (l *Login) handleExternalUserAuthenticated( // * external not found overview: // - creation by user // - linking to existing user -func (l *Login) externalUserNotExisting(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, provider *query.IDPTemplate, externalUser *domain.ExternalUser) { +func (l *Login) externalUserNotExisting(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, provider *query.IDPTemplate, externalUser *domain.ExternalUser, changed bool) { resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { @@ -360,6 +360,12 @@ func (l *Login) externalUserNotExisting(w http.ResponseWriter, r *http.Request, l.renderExternalNotFoundOption(w, r, authReq, orgIAMPolicy, human, idpLink, err) return } + if changed { + if err := l.authRepo.SetLinkingUser(r.Context(), authReq, externalUser); err != nil { + l.renderError(w, r, authReq, err) + return + } + } l.autoCreateExternalUser(w, r, authReq) } diff --git a/internal/auth/repository/auth_request.go b/internal/auth/repository/auth_request.go index 17f889365..ffa70f9d6 100644 --- a/internal/auth/repository/auth_request.go +++ b/internal/auth/repository/auth_request.go @@ -17,6 +17,7 @@ type AuthRequestRepository interface { CheckLoginName(ctx context.Context, id, loginName, userAgentID string) error CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser, info *domain.BrowserInfo) error SetExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser) error + SetLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error SelectUser(ctx context.Context, id, userID, userAgentID string) error SelectExternalIDP(ctx context.Context, authReqID, idpConfigID, userAgentID string) error VerifyPassword(ctx context.Context, id, userID, resourceOwner, password, userAgentID string, info *domain.BrowserInfo) error diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index fcabd8f77..6e7de351e 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -278,6 +278,16 @@ func (repo *AuthRequestRepo) SetExternalUserLogin(ctx context.Context, authReqID return repo.AuthRequests.UpdateAuthRequest(ctx, request) } +func (repo *AuthRequestRepo) SetLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error { + for i, user := range request.LinkingUsers { + if user.ExternalUserID == externalUser.ExternalUserID { + request.LinkingUsers[i] = externalUser + return repo.AuthRequests.UpdateAuthRequest(ctx, request) + } + } + return nil +} + func (repo *AuthRequestRepo) setLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error { request.LinkingUsers = append(request.LinkingUsers, externalUser) return repo.AuthRequests.UpdateAuthRequest(ctx, request) From 31534c659edb6d4b9dfa47ac5eb2bf555b9bd6f0 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 13 Jun 2023 07:37:22 +0200 Subject: [PATCH 20/76] fix(console): url safe base64 to array buffer (#6019) fix: console base64 to array buffer Co-authored-by: Livio Spring --- .../app/pages/users/user-detail/u2f-util.ts | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/console/src/app/pages/users/user-detail/u2f-util.ts b/console/src/app/pages/users/user-detail/u2f-util.ts index b19c61a54..fab5a8aff 100644 --- a/console/src/app/pages/users/user-detail/u2f-util.ts +++ b/console/src/app/pages/users/user-detail/u2f-util.ts @@ -1,9 +1,31 @@ -export function _base64ToArrayBuffer(base64: string): any { - const binaryString = atob(base64); - const len = binaryString.length; - const bytes = new Uint8Array(len); - for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i); +export function _base64ToArrayBuffer(thing: any) { + if (typeof thing === 'string') { + // base64url to base64 + thing = thing.replace(/-/g, '+').replace(/_/g, '/'); + + // base64 to Uint8Array + var str = window.atob(thing); + var bytes = new Uint8Array(str.length); + for (var i = 0; i < str.length; i++) { + bytes[i] = str.charCodeAt(i); + } + thing = bytes; } - return bytes.buffer; + + // Array to Uint8Array + if (Array.isArray(thing)) { + thing = new Uint8Array(thing); + } + + // Uint8Array to ArrayBuffer + if (thing instanceof Uint8Array) { + thing = thing.buffer; + } + + // error if none of the above worked + if (!(thing instanceof ArrayBuffer)) { + throw new TypeError('could not coerce to ArrayBuffer'); + } + + return thing; } From 74198b0078dd7d95d924191fcc4834b6b275d900 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 16 Jun 2023 10:27:43 +0200 Subject: [PATCH 21/76] fix: external user check (#6038) --- internal/api/ui/login/external_provider_handler.go | 2 +- internal/domain/auth_request.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index abd07a70e..d54d29b4e 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -288,7 +288,7 @@ func (l *Login) handleExternalUserAuthenticated( externalUser := mapIDPUserToExternalUser(user, provider.ID) // check and fill in local linked user externalErr := l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, externalUser, domain.BrowserInfoFromRequest(r)) - if !errors.IsNotFound(externalErr) { + if externalErr != nil && !errors.IsNotFound(externalErr) { l.renderError(w, r, authReq, externalErr) return } diff --git a/internal/domain/auth_request.go b/internal/domain/auth_request.go index 86cd0575f..c0b9e1a40 100644 --- a/internal/domain/auth_request.go +++ b/internal/domain/auth_request.go @@ -42,7 +42,7 @@ type AuthRequest struct { PrivateLabelingSetting PrivateLabelingSetting SelectedIDPConfigID string LinkingUsers []*ExternalUser - PossibleSteps []NextStep + PossibleSteps []NextStep `json:"-"` PasswordVerified bool MFAsVerified []MFAType Audience []string From 8041dd995cf840b0d5d97ca18097b7c9f1ed84aa Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 21 Jun 2023 07:00:03 +0200 Subject: [PATCH 22/76] fix(login): ensure auth request information is up-to-date in external user check (#6060) --- .../api/ui/login/external_provider_handler.go | 15 +++++++-------- .../eventsourcing/eventstore/auth_request.go | 2 ++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index d54d29b4e..38029ad05 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -292,6 +292,13 @@ func (l *Login) handleExternalUserAuthenticated( l.renderError(w, r, authReq, externalErr) return } + var err error + // read current auth request state (incl. authorized user) + authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) + if err != nil { + l.renderError(w, r, authReq, err) + return + } externalUser, externalUserChange, err := l.runPostExternalAuthenticationActions(externalUser, tokens(session), authReq, r, user, nil) if err != nil { l.renderError(w, r, authReq, err) @@ -302,14 +309,6 @@ func (l *Login) handleExternalUserAuthenticated( l.externalUserNotExisting(w, r, authReq, provider, externalUser, externalUserChange) return } - if provider.IsAutoUpdate || len(externalUser.Metadatas) > 0 || externalUserChange { - // read current auth request state (incl. authorized user) - authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) - if err != nil { - l.renderError(w, r, authReq, err) - return - } - } if provider.IsAutoUpdate || externalUserChange { err = l.updateExternalUser(r.Context(), authReq, externalUser) if err != nil { diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 6e7de351e..4de5b0237 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -247,6 +247,8 @@ func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReq } err = repo.checkExternalUserLogin(ctx, request, externalUser.IDPConfigID, externalUser.ExternalUserID) if errors.IsNotFound(err) { + // clear potential user information (e.g. when username was entered but another external user was returned) + request.SetUserInfo("", "", "", "", "", request.UserOrgID) if err := repo.setLinkingUser(ctx, request, externalUser); err != nil { return err } From c8b4ef28bd315e35b43bc931f2d31b2445242b51 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Sat, 24 Jun 2023 08:54:25 +0200 Subject: [PATCH 23/76] fix(migration): speed up update and add logs --- cmd/setup/10.go | 2 +- cmd/setup/11.go | 36 +++++++++++++++++++++----------- cmd/setup/11/11_fetch_events.sql | 3 ++- cmd/setup/11/11_fill_column.sql | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cmd/setup/10.go b/cmd/setup/10.go index a83113cb1..16d14d3d0 100644 --- a/cmd/setup/10.go +++ b/cmd/setup/10.go @@ -35,7 +35,7 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { defer cancel() for { - affected := int64(0) + var affected int64 err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { if mig.dbClient.Type() == "cockroach" { if _, err := tx.Exec("SET experimental_enable_temp_tables=on"); err != nil { diff --git a/cmd/setup/11.go b/cmd/setup/11.go index 544188d12..c3ddadfff 100644 --- a/cmd/setup/11.go +++ b/cmd/setup/11.go @@ -38,24 +38,34 @@ type AddEventCreatedAt struct { func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { // execute step 10 again because events created after the first execution of step 10 // could still have the wrong ordering of sequences and creation date + logging.WithFields("step", "11").Info("ensure creation dates order") if err := mig.step10.Execute(ctx); err != nil { + logging.WithFields("step", "11").WithError(err).Info("ensure creation dates order failed") return err } + logging.WithFields("step", "11").Info("ensure creation dates order done") + logging.WithFields("step", "11").Info("add created_at column") _, err := mig.dbClient.ExecContext(ctx, addEventCreatedAt) if err != nil { + logging.WithFields("step", "11").WithError(err).Info("add created_at column failed") return err } + logging.WithFields("step", "11").Info("created_at column added") createIndex, err := readStmt(createdAtIndexCreateStmt, "11", mig.dbClient.Type(), "create_index.sql") if err != nil { return err } + logging.WithFields("step", "11").Info("create index") _, err = mig.dbClient.ExecContext(ctx, createIndex) + logging.WithFields("step", "11").WithError(err).Info("create index failed") if err != nil { return err } + logging.WithFields("step", "11").Info("index created") - for { + for i := 0; ; i++ { + logging.WithFields("step", "11", "iteration", i).Info("begin set created_at iteration") var count int err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { rows, err := tx.Query(fetchCreatedAt, mig.BulkAmount) @@ -64,28 +74,30 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { } defer rows.Close() - data := make(map[string]time.Time, 20) + type date struct { + instanceID string + eventSequence uint64 + creationDate time.Time + } + dates := make([]*date, 0, 20) + for rows.Next() { count++ - var ( - id string - creationDate time.Time - ) - err = rows.Scan(&id, &creationDate) + d := new(date) + err = rows.Scan(&d.instanceID, &d.eventSequence, &d.creationDate) if err != nil { return err } - - data[id] = creationDate + dates = append(dates, d) } if err := rows.Err(); err != nil { return err } - for id, creationDate := range data { - _, err = tx.Exec(fillCreatedAt, creationDate, id) + for _, d := range dates { + _, err = tx.Exec(fillCreatedAt, d.creationDate, d.instanceID, d.eventSequence) if err != nil { return err } @@ -96,7 +108,7 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { if err != nil { return err } - logging.WithFields("count", count).Info("creation dates set") + logging.WithFields("step", "11", "iteration", i, "count", count).Info("set created_at iteration done") if count < 20 { break } diff --git a/cmd/setup/11/11_fetch_events.sql b/cmd/setup/11/11_fetch_events.sql index f66882675..64add9340 100644 --- a/cmd/setup/11/11_fetch_events.sql +++ b/cmd/setup/11/11_fetch_events.sql @@ -1,5 +1,6 @@ SELECT - id + instance_id + , event_sequence , creation_date FROM eventstore.events diff --git a/cmd/setup/11/11_fill_column.sql b/cmd/setup/11/11_fill_column.sql index 0a16913db..68265e6a7 100644 --- a/cmd/setup/11/11_fill_column.sql +++ b/cmd/setup/11/11_fill_column.sql @@ -1 +1 @@ -UPDATE eventstore.events SET created_at = $1 WHERE id = $2 \ No newline at end of file +UPDATE eventstore.events SET created_at = $1 WHERE instance_id = $2 AND event_sequence = $3 \ No newline at end of file From 07fb6f828472e6de53ef9ce8f6b4d3b613375375 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 9 Jun 2023 15:14:15 +0200 Subject: [PATCH 24/76] fix(console): fallback to default language if browser language not supported (#5999) fix: fallback to default language if browser language not supported --- console/src/app/app.component.ts | 11 ++++++----- console/src/app/services/grpc.service.ts | 6 +++++- console/src/app/utils/language.ts | 3 +++ 3 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 console/src/app/utils/language.ts diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index 9c473a288..7249dad12 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -19,6 +19,7 @@ import { KeyboardShortcutsService } from './services/keyboard-shortcuts/keyboard import { ManagementService } from './services/mgmt.service'; import { ThemeService } from './services/theme.service'; import { UpdateService } from './services/update.service'; +import { fallbackLanguage, supportedLanguages, supportedLanguagesRegexp } from './utils/language'; @Component({ selector: 'cnsl-root', @@ -267,15 +268,15 @@ export class AppComponent implements OnDestroy { } private setLanguage(): void { - this.translate.addLangs(['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']); - this.translate.setDefaultLang('en'); + this.translate.addLangs(supportedLanguages); + this.translate.setDefaultLang(fallbackLanguage); this.authService.user.subscribe((userprofile) => { if (userprofile) { - const cropped = navigator.language.split('-')[0] ?? 'en'; - const fallbackLang = cropped.match(/de|en|es|fr|it|ja|pl|zh/) ? cropped : 'en'; + const cropped = navigator.language.split('-')[0] ?? fallbackLanguage; + const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage; - const lang = userprofile?.human?.profile?.preferredLanguage.match(/de|en|es|fr|it|ja|pl|zh/) + const lang = userprofile?.human?.profile?.preferredLanguage.match(supportedLanguagesRegexp) ? userprofile.human.profile?.preferredLanguage : fallbackLang; this.translate.use(lang); diff --git a/console/src/app/services/grpc.service.ts b/console/src/app/services/grpc.service.ts index a842edfb2..e8897fbc3 100644 --- a/console/src/app/services/grpc.service.ts +++ b/console/src/app/services/grpc.service.ts @@ -8,6 +8,7 @@ import { catchError, switchMap, tap, throwError } from 'rxjs'; import { AdminServiceClient } from '../proto/generated/zitadel/AdminServiceClientPb'; import { AuthServiceClient } from '../proto/generated/zitadel/AuthServiceClientPb'; import { ManagementServiceClient } from '../proto/generated/zitadel/ManagementServiceClientPb'; +import { fallbackLanguage, supportedLanguagesRegexp } from '../utils/language'; import { AuthenticationService } from './authentication.service'; import { EnvironmentService } from './environment.service'; import { ExhaustedService } from './exhausted.service'; @@ -40,8 +41,11 @@ export class GrpcService { public loadAppEnvironment(): Promise { this.themeService.applyLabelPolicy(); // We use the browser language until we can make API requests to get the users configured language. + + const browserLanguage = this.translate.getBrowserLang(); + const language = browserLanguage?.match(supportedLanguagesRegexp) ? browserLanguage : fallbackLanguage; return this.translate - .use(this.translate.getBrowserLang() || this.translate.defaultLang) + .use(language || this.translate.defaultLang) .pipe( switchMap(() => this.envService.env), tap((env) => { diff --git a/console/src/app/utils/language.ts b/console/src/app/utils/language.ts new file mode 100644 index 000000000..d4c54f375 --- /dev/null +++ b/console/src/app/utils/language.ts @@ -0,0 +1,3 @@ +export const supportedLanguages = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; +export const supportedLanguagesRegexp: RegExp = /de|en|es|fr|it|ja|pl|zh/; +export const fallbackLanguage: string = 'en'; From 9020c9d94f9e9c757cc3c10ec34782576784df0e Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 27 Jun 2023 10:48:49 +0200 Subject: [PATCH 25/76] fix(migration): speed up step 11 (#6085) --- cmd/setup/11.go | 56 +++++-------------------------- cmd/setup/11/11_fetch_events.sql | 13 ------- cmd/setup/11/11_fill_column.sql | 1 - cmd/setup/11/11_update_events.sql | 21 ++++++++++++ 4 files changed, 29 insertions(+), 62 deletions(-) delete mode 100644 cmd/setup/11/11_fetch_events.sql delete mode 100644 cmd/setup/11/11_fill_column.sql create mode 100644 cmd/setup/11/11_update_events.sql diff --git a/cmd/setup/11.go b/cmd/setup/11.go index c3ddadfff..77d42bb88 100644 --- a/cmd/setup/11.go +++ b/cmd/setup/11.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "embed" - "time" "github.com/cockroachdb/cockroach-go/v2/crdb" "github.com/zitadel/logging" @@ -15,10 +14,8 @@ import ( var ( //go:embed 11/11_add_column.sql addEventCreatedAt string - //go:embed 11/11_fetch_events.sql - fetchCreatedAt string - //go:embed 11/11_fill_column.sql - fillCreatedAt string + //go:embed 11/11_update_events.sql + setCreatedAt string //go:embed 11/11_set_column.sql setCreatedAtDetails string //go:embed 11/postgres/create_index.sql @@ -38,70 +35,33 @@ type AddEventCreatedAt struct { func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { // execute step 10 again because events created after the first execution of step 10 // could still have the wrong ordering of sequences and creation date - logging.WithFields("step", "11").Info("ensure creation dates order") if err := mig.step10.Execute(ctx); err != nil { - logging.WithFields("step", "11").WithError(err).Info("ensure creation dates order failed") return err } - logging.WithFields("step", "11").Info("ensure creation dates order done") - logging.WithFields("step", "11").Info("add created_at column") _, err := mig.dbClient.ExecContext(ctx, addEventCreatedAt) if err != nil { - logging.WithFields("step", "11").WithError(err).Info("add created_at column failed") return err } - logging.WithFields("step", "11").Info("created_at column added") createIndex, err := readStmt(createdAtIndexCreateStmt, "11", mig.dbClient.Type(), "create_index.sql") if err != nil { return err } - logging.WithFields("step", "11").Info("create index") _, err = mig.dbClient.ExecContext(ctx, createIndex) - logging.WithFields("step", "11").WithError(err).Info("create index failed") if err != nil { return err } - logging.WithFields("step", "11").Info("index created") for i := 0; ; i++ { - logging.WithFields("step", "11", "iteration", i).Info("begin set created_at iteration") - var count int - err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { - rows, err := tx.Query(fetchCreatedAt, mig.BulkAmount) + var count int64 + err := crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { + res, err := tx.Exec(setCreatedAt, mig.BulkAmount) if err != nil { return err } - defer rows.Close() - type date struct { - instanceID string - eventSequence uint64 - creationDate time.Time - } - dates := make([]*date, 0, 20) - - for rows.Next() { - count++ - - d := new(date) - err = rows.Scan(&d.instanceID, &d.eventSequence, &d.creationDate) - if err != nil { - return err - } - dates = append(dates, d) - - } - if err := rows.Err(); err != nil { - return err - } - - for _, d := range dates { - _, err = tx.Exec(fillCreatedAt, d.creationDate, d.instanceID, d.eventSequence) - if err != nil { - return err - } - } + count, _ = res.RowsAffected() + logging.WithFields("affected", count).Info("created_at updated") return nil }) @@ -109,7 +69,7 @@ func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { return err } logging.WithFields("step", "11", "iteration", i, "count", count).Info("set created_at iteration done") - if count < 20 { + if count < int64(mig.BulkAmount) { break } } diff --git a/cmd/setup/11/11_fetch_events.sql b/cmd/setup/11/11_fetch_events.sql deleted file mode 100644 index 64add9340..000000000 --- a/cmd/setup/11/11_fetch_events.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT - instance_id - , event_sequence - , creation_date -FROM - eventstore.events -WHERE - created_at IS NULL -ORDER BY - event_sequence DESC - , instance_id -LIMIT $1 -FOR UPDATE \ No newline at end of file diff --git a/cmd/setup/11/11_fill_column.sql b/cmd/setup/11/11_fill_column.sql deleted file mode 100644 index 68265e6a7..000000000 --- a/cmd/setup/11/11_fill_column.sql +++ /dev/null @@ -1 +0,0 @@ -UPDATE eventstore.events SET created_at = $1 WHERE instance_id = $2 AND event_sequence = $3 \ No newline at end of file diff --git a/cmd/setup/11/11_update_events.sql b/cmd/setup/11/11_update_events.sql new file mode 100644 index 000000000..15a3df8a0 --- /dev/null +++ b/cmd/setup/11/11_update_events.sql @@ -0,0 +1,21 @@ +UPDATE eventstore.events SET + created_at = creation_date +FROM ( + SELECT + e.event_sequence as seq + , e.instance_id as i_id + , e.creation_date as cd + FROM + eventstore.events e + WHERE + created_at IS NULL + ORDER BY + event_sequence ASC + , instance_id + LIMIT $1 +) AS e +WHERE + e.seq = eventstore.events.event_sequence + AND e.i_id = eventstore.events.instance_id + AND e.cd = eventstore.events.creation_date +; \ No newline at end of file From e08b8dcb13e25fda2a7883fc9893ef89f5d04459 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 21 Jul 2023 08:54:38 +0200 Subject: [PATCH 26/76] chore: linting in console (#6243) chore: linting in console (cherry picked from commit cfb8f3c07ffd86ae3cbba1ee2d2da6a60899bbb2) --- console/src/app/app.module.ts | 2 +- console/src/assets/i18n/de.json | 2 +- console/src/assets/i18n/en.json | 2 +- console/src/assets/i18n/es.json | 2 +- console/src/assets/i18n/fr.json | 2 +- console/src/assets/i18n/it.json | 2 +- console/src/assets/i18n/ja.json | 2 +- console/src/assets/i18n/pl.json | 2 +- console/src/assets/i18n/pt.json | 4378 +++++++++++++++---------------- console/src/assets/i18n/zh.json | 2 +- 10 files changed, 2198 insertions(+), 2198 deletions(-) diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts index 793dfbc22..3781c3357 100644 --- a/console/src/app/app.module.ts +++ b/console/src/app/app.module.ts @@ -1,6 +1,5 @@ import { CommonModule, registerLocaleData } from '@angular/common'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; -import localePt from '@angular/common/locales/pt'; import localeBg from '@angular/common/locales/bg'; import localeDe from '@angular/common/locales/de'; import localeEn from '@angular/common/locales/en'; @@ -10,6 +9,7 @@ import localeIt from '@angular/common/locales/it'; import localeJa from '@angular/common/locales/ja'; import localeMk from '@angular/common/locales/mk'; import localePl from '@angular/common/locales/pl'; +import localePt from '@angular/common/locales/pt'; import localeZh from '@angular/common/locales/zh'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { MatNativeDateModule } from '@angular/material/core'; diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 18d83c3a4..87b6e83fd 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1041,7 +1041,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 8ee10db36..c858d5acb 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1042,7 +1042,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index 9f4fe349f..3a0df8980 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -1042,7 +1042,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 8abce8f33..a81c6eae5 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1041,7 +1041,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 22e4df93c..387bc187d 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1041,7 +1041,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 498d2b805..8ac4692d3 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1042,7 +1042,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 33d4fa741..774038f7c 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1041,7 +1041,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 261061422..00607c159 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -1,2190 +1,2190 @@ { - "APP_NAME": "ZITADEL", - "PAGINATOR": { - "PREVIOUS": "Anterior", - "NEXT": "Próximo", - "COUNT": "Total de Resultados", - "MORE": "Mais" - }, - "FOOTER": { - "LINKS": { - "CONTACT": "Contato", - "TOS": "Termos de Serviço", - "PP": "Política de Privacidade" - }, - "THEME": { - "DARK": "Escuro", - "LIGHT": "Claro" - } - }, - "HOME": { - "WELCOME": "Comece com ZITADEL", - "DISCLAIMER": "A ZITADEL trata seus dados confidencialmente e com segurança.", - "DISCLAIMERLINK": "Mais informações", - "DOCUMENTATION": { - "TITLE": "Documentação", - "DESCRIPTION": "Comece rapidamente com ZITADEL." - }, - "GETSTARTED": { - "TITLE": "Comece com ZITADEL", - "DESCRIPTION": "Comece rapidamente com ZITADEL." - }, - "QUICKSTARTS": { - "LABEL": "Primeiros Passos", - "TITLE": "Início Rápido", - "DESCRIPTION": "Comece rapidamente com ZITADEL." - }, - "SHORTCUTS": { - "SHORTCUTS": "Atalhos", - "SETTINGS": "Atalhos disponíveis", - "PROJECTS": "Projetos", - "REORDER": "Mantenha e arraste o bloco para movê-lo", - "ADD": "Mantenha e arraste um bloco para adicionar" - } - }, - "ONBOARDING": { - "DESCRIPTION": "Seu processo de integração", - "MOREDESCRIPTION": "mais atalhos", - "COMPLETED": "concluído", - "DISMISS": "Não, obrigado, sou um profissional.", - "CARD": { - "TITLE": "Inicie o ZITADEL", - "DESCRIPTION": "Esta lista de verificação ajuda a configurar sua instância e orienta você nas etapas mais essenciais" - }, - "EVENTS": { - "instance.policy.label.added": { - "title": "Configure sua marca", - "description": "Defina cores e forma para o seu login e faça o upload do seu logotipo e ícones.", - "action": "Configurar marca" - }, - "instance.smtp.config.added": { - "title": "Configure as configurações SMTP", - "description": "Configure as configurações do seu próprio servidor de e-mail.", - "action": "Configurar SMTP" - }, - "project.added": { - "title": "Crie um projeto", - "description": "Adicione um projeto e defina suas funções e autorizações.", - "action": "Criar projeto" - }, - "project.application.added": { - "title": "Crie um aplicativo", - "description": "Crie um aplicativo da web, nativo, API ou SAML e configure o fluxo de autenticação.", - "action": "Criar aplicativo" - }, - "user.human.added": { - "title": "Adicione usuários", - "description": "Adicione usuários do seu aplicativo", - "action": "Adicionar usuário" - }, - "user.grant.added": { - "title": "Conceda permissões aos usuários", - "description": "Permita que os usuários acessem seu aplicativo e configure suas funções.", - "action": "Conceder permissões" - } - } - }, - "MENU": { - "INSTANCE": "Instância", - "DASHBOARD": "Início", - "PERSONAL_INFO": "Informações Pessoais", - "DOCUMENTATION": "Documentação", - "INSTANCEOVERVIEW": "Instância", - "ORGS": "Organizações", - "VIEWS": "Visualizações", - "EVENTS": "Eventos", - "FAILEDEVENTS": "Eventos com Falha", - "ORGANIZATION": "Organização", - "DOMAINS": "Domínios", - "PROJECT": "Projetos", - "PROJECTOVERVIEW": "Visão Geral", - "PROJECTGRANTS": "Autorizações", - "ROLES": "Funções", - "GRANTEDPROJECT": "Projetos Concedidos", - "HUMANUSERS": "Usuários", - "MACHINEUSERS": "Usuários de Serviço", - "LOGOUT": "Encerrar Sessão de Todos os Usuários", - "NEWORG": "Nova Organização", - "IAMADMIN": "Você é um Administrador IAM. Observe que você possui permissões estendidas.", - "SHOWORGS": "Mostrar Todas as Organizações", - "GRANTS": "Autorizações", - "ACTIONS": "Ações", - "PRIVACY": "Privacidade", - "TOS": "Termos de Serviço", - "OPENSHORTCUTSTOOLTIP": "Digite ? para mostrar os atalhos do teclado", - "SETTINGS": "Configurações", - "CUSTOMERPORTAL": "Portal do Cliente" - }, - "ACTIONS": { - "ACTIONS": "Ações", - "FILTER": "Filtrar", - "RENAME": "Renomear", - "SET": "Definir", - "COPY": "Copiar para a Área de Transferência", - "COPIED": "Copiado para a área de transferência.", - "RESET": "Redefinir", - "RESETDEFAULT": "Redefinir para o Padrão", - "RESETTO": "Redefinir para: ", - "RESETCURRENT": "Redefinir para o atual", - "SHOW": "Mostrar", - "HIDE": "Ocultar", - "SAVE": "Salvar", - "SAVENOW": "Salvar agora", - "NEW": "Novo", - "ADD": "Adicionar", - "CREATE": "Criar", - "CONTINUE": "Continuar", - "BACK": "Voltar", - "CLOSE": "Fechar", - "CLEAR": "Limpar", - "CANCEL": "Cancelar", - "INFO": "Informações", - "OK": "OK", - "SELECT": "Selecionar", - "VIEW": "Mostrar", - "SELECTIONDELETE": "Excluir seleção", - "DELETE": "Excluir", - "REMOVE": "Remover", - "VERIFY": "Verificar", - "FINISH": "Concluir", - "FINISHED": "Fechar", - "CHANGE": "Alterar", - "REACTIVATE": "Reativar", - "ACTIVATE": "Ativar", - "DEACTIVATE": "Desativar", - "REFRESH": "Atualizar", - "LOGIN": "Entrar", - "EDIT": "Editar", - "PIN": "Fixar / Desafixar", - "CONFIGURE": "Configurar", - "SEND": "Enviar", - "NEWVALUE": "Novo Valor", - "RESTORE": "Restaurar", - "CONTINUEWITHOUTSAVE": "Continuar sem salvar", - "OF": "de", - "PREVIOUS": "Anterior", - "NEXT": "Próximo", - "MORE": "mais", - "STEP": "Passo", - "SETUP": "Configuração", - "UNSAVEDCHANGES": "Alterações não salvas", - "UNSAVED": { - "DIALOG": { - "DESCRIPTION": "Tem certeza de que deseja descartar esta nova ação? Sua ação será perdida", - "CANCEL": "Cancelar", - "DISCARD": "Descartar" - } - }, - "TABLE": { - "SHOWUSER": "Mostrar usuário {{value}}" - } - }, - "MEMBERROLES": { - "IAM_OWNER": "Tem controle sobre toda a instância, incluindo todas as organizações", - "IAM_OWNER_VIEWER": "Tem permissão para revisar toda a instância, incluindo todas as organizações", - "IAM_ORG_MANAGER": "Tem permissão para criar e gerenciar organizações", - "IAM_USER_MANAGER": "Tem permissão para criar e gerenciar usuários", - "ORG_OWNER": "Tem permissão sobre toda a organização", - "ORG_USER_MANAGER": "Tem permissão para criar e gerenciar usuários da organização", - "ORG_OWNER_VIEWER": "Tem permissão para revisar toda a organização", - "ORG_USER_PERMISSION_EDITOR": "Tem permissão para gerenciar concessões de usuários", - "ORG_PROJECT_PERMISSION_EDITOR": "Tem permissão para gerenciar concessões de projetos", - "ORG_PROJECT_CREATOR": "Tem permissão para criar seus próprios projetos e configurações subjacentes", - "PROJECT_OWNER": "Tem permissão sobre todo o projeto", - "PROJECT_OWNER_VIEWER": "Tem permissão para revisar todo o projeto", - "PROJECT_OWNER_GLOBAL": "Tem permissão sobre todo o projeto", - "PROJECT_OWNER_VIEWER_GLOBAL": "Tem permissão para revisar todo o projeto", - "PROJECT_GRANT_OWNER": "Tem permissão para gerenciar a concessão do projeto", - "PROJECT_GRANT_OWNER_VIEWER": "Tem permissão para revisar a concessão do projeto" - }, - "OVERLAYS": { - "ORGSWITCHER": { - "TEXT": "Todas as configurações e tabelas de organização no console são baseadas em uma organização selecionada. Clique neste botão para trocar de organização ou criar uma nova." - }, - "INSTANCE": { - "TEXT": "Clique aqui para acessar as configurações da instância. Observe que você só tem acesso a este botão se tiver permissões avançadas." - }, - "PROFILE": { - "TEXT": "Aqui você pode alternar entre suas contas de usuário e gerenciar suas sessões e perfil." - }, - "NAV": { - "TEXT": "Esta navegação muda com base na organização selecionada acima ou na sua instância" - }, - "CONTEXTCHANGED": { - "TEXT": "O contexto da organização foi alterado." - }, - "SWITCHEDTOINSTANCE": { - "TEXT": "A visualização mudou para a instância!" - } - }, - "FILTER": { - "TITLE": "Filtro", - "STATE": "Status", - "DISPLAYNAME": "Nome de Exibição do Usuário", - "EMAIL": "E-mail", - "USERNAME": "Nome de Usuário", - "ORGNAME": "Nome da Organização", - "PROJECTNAME": "Nome do Projeto", - "RESOURCEOWNER": "Proprietário do Recurso", - "METHODS": { - "5": "contém", - "7": "termina com", - "1": "igual a" - } - }, - "KEYBOARDSHORTCUTS": { - "TITLE": "Atalhos de Teclado", - "UNDERORGCONTEXT": "Dentro das páginas da organização", - "SIDEWIDE": "Atalhos em todo o site", - "SHORTCUTS": { - "HOME": "Ir para Início", - "INSTANCE": "Ir para Instância", - "ORG": "Ir para Organização", - "ORGSETTINGS": "Ir para Configurações da Organização", - "ORGSWITCHER": "Trocar de Organização", - "ME": "Ir para meu perfil", - "PROJECTS": "Ir para Projetos", - "USERS": "Ir para Usuários", - "USERGRANTS": "Ir para Autorizações", - "ACTIONS": "Ir para Ações e Fluxos", - "DOMAINS": "Ir para Domínios" - } - }, - "RESOURCEID": "ID do Recurso", - "NAME": "Nome", - "VERSION": "Versão", - "TABLE": { - "NOROWS": "Sem dados" - }, - "ERRORS": { - "REQUIRED": "Por favor, preencha este campo.", - "ATLEASTONE": "Forneça pelo menos um valor.", - "TOKENINVALID": { - "TITLE": "Seu token de autorização expirou.", - "DESCRIPTION": "Clique no botão abaixo para fazer login novamente." - }, - "EXHAUSTED": { - "TITLE": "Sua cota para solicitações autenticadas está esgotada.", - "DESCRIPTION": "Remova ou aumente o limite de cota para esta instância ZITADEL." - }, - "INVALID_FORMAT": "O formato é inválido.", - "NOTANEMAIL": "O valor fornecido não é um endereço de e-mail.", - "MINLENGTH": "Deve ter pelo menos {{requiredLength}} caracteres.", - "UPPERCASEMISSING": "Deve incluir uma letra maiúscula.", - "LOWERCASEMISSING": "Deve incluir uma letra minúscula.", - "SYMBOLERROR": "Deve incluir um símbolo ou caractere de pontuação.", - "NUMBERERROR": "Deve incluir um dígito.", - "PWNOTEQUAL": "As senhas fornecidas não correspondem.", - "PHONE": "O número de telefone deve começar com 00 ou +." - }, - "USER": { - "SETTINGS": { - "TITLE": "Configurações", - "GENERAL": "Geral", - "IDP": "Provedores de Identidade", - "SECURITY": "Senha e Segurança", - "KEYS": "Chaves", - "PAT": "Tokens de Acesso Pessoal", - "USERGRANTS": "Autorizações", - "MEMBERSHIPS": "Associações", - "METADATA": "Metadados" - }, - "TITLE": "Informações Pessoais", - "DESCRIPTION": "Gerencie suas informações e configurações de segurança.", - "PAGES": { - "LIST": "Usuários", - "TITLE": "Usuário", - "DESCRIPTION": "Crie novos usuários em sua organização e gerencie os existentes.", - "LISTMACHINE": "Usuários de Serviço", - "DESCRIPTIONMACHINE": "Crie e gerencie Usuários de Serviço de sua organização", - "DETAIL": "Detalhes", - "CREATE": "Criar", - "MY": "Minhas Informações", - "LOGINNAMES": "Nomes de Login", - "LOGINMETHODS": "Métodos de Login", - "LOGINNAMESDESC": "Estes são os seus nomes de login:", - "NOUSER": "Nenhum usuário associado.", - "REACTIVATE": "Reativar", - "DEACTIVATE": "Desativar", - "FILTER": "Filtro", - "STATE": "Status", - "DELETE": "Excluir Usuário", - "UNLOCK": "Desbloquear Usuário", - "GENERATESECRET": "Gerar Segredo do Cliente", - "REMOVESECRET": "Remover Segredo do Cliente", - "LOCKEDDESCRIPTION": "Este usuário foi bloqueado devido a exceder o número máximo de tentativas de login e deve ser desbloqueado para ser usado novamente.", - "DELETEACCOUNT": "Excluir Conta", - "DELETEACCOUNT_DESC": "Se você realizar esta ação, será desconectado e não terá mais acesso à sua conta. Essa ação não pode ser desfeita, portanto, prossiga com cautela.", - "DELETEACCOUNT_BTN": "Excluir Conta", - "DELETEACCOUNT_SUCCESS": "Conta excluída com sucesso!" - }, - "DETAILS": { - "DATECREATED": "Criado", - "DATECHANGED": "Alterado" - }, - "DIALOG": { - "DELETE_TITLE": "Excluir Usuário", - "DELETE_SELF_TITLE": "Excluir Conta", - "DELETE_DESCRIPTION": "Você está prestes a excluir permanentemente um usuário. Tem certeza?", - "DELETE_SELF_DESCRIPTION": "Você está prestes a excluir permanentemente sua conta pessoal. Isso irá desconectá-lo e excluir seu usuário. Essa ação não pode ser desfeita!", - "DELETE_AUTH_DESCRIPTION": "Você está prestes a excluir permanentemente sua conta pessoal. Tem certeza?", - "TYPEUSERNAME": "Digite '{{value}}' para confirmar e excluir o usuário.", - "USERNAME": "Nome de usuário", - "DELETE_BTN": "Excluir permanentemente" - }, - "SENDEMAILDIALOG": { - "TITLE": "Enviar Notificação por E-mail", - "DESCRIPTION": "Clique no botão abaixo para enviar uma notificação para o endereço de e-mail atual ou altere o endereço de e-mail no campo.", - "NEWEMAIL": "Novo endereço de e-mail" - }, - "SECRETDIALOG": { - "CLIENTSECRET": "Segredo do Cliente", - "CLIENTSECRET_DESCRIPTION": "Mantenha seu segredo do cliente em um local seguro, pois ele desaparecerá assim que o diálogo for fechado." - }, - "TABLE": { - "DEACTIVATE": "Desativar", - "ACTIVATE": "Ativar", - "CHANGEDATE": "Última Modificação", - "CREATIONDATE": "Criado Em", - "TYPES": { - "HUMAN": "Usuários", - "MACHINE": "Usuários de Serviço" - }, - "FILTER": { - "0": "Filtrar por Nome de Exibição", - "1": "Filtrar por Nome de Usuário", - "2": "filtrar por Nome de Exibição", - "3": "filtrar por Nome de Usuário", - "4": "filtrar por E-mail", - "5": "filtrar por Nome de Exibição", - "10": "filtrar por nome de organização", - "12": "filtrar por nome de projeto" - }, - "EMPTY": "Nenhum registro" - }, - "PASSWORDLESS": { - "SEND": "Enviar link de registro", - "TABLETYPE": "Tipo", - "TABLESTATE": "Status", - "NAME": "Nome", - "EMPTY": "Nenhum dispositivo configurado", - "TITLE": "Autenticação sem Senha", - "DESCRIPTION": "Adicione Métodos de Autenticação baseados em WebAuthn para fazerlogin no ZITADEL sem senha.", - "MANAGE_DESCRIPTION": "Gerencie os métodos de segundo fator dos seus usuários.", - "U2F": "Adicionar método", - "U2F_DIALOG_TITLE": "Verificar autenticador", - "U2F_DIALOG_DESCRIPTION": "Digite um nome para seu login sem senha usado", - "U2F_SUCCESS": "Autenticação sem senha criada com sucesso!", - "U2F_ERROR": "Ocorreu um erro durante a configuração!", - "U2F_NAME": "Nome do Autenticador", - "TYPE": { - "0": "Nenhum MFA definido", - "1": "One Time Password (OTP)", - "2": "Impressão digital, Chaves de Segurança, Face ID e outros" - }, - "STATE": { - "0": "Nenhum Estado", - "1": "Não Pronto", - "2": "Pronto", - "3": "Excluído" - }, - "DIALOG": { - "DELETE_TITLE": "Remover Método de Autenticação sem Senha", - "DELETE_DESCRIPTION": "Você está prestes a excluir um método de autenticação sem senha. Tem certeza?", - "ADD_TITLE": "Autenticação sem senha", - "ADD_DESCRIPTION": "Selecione uma das opções disponíveis para criar um método de autenticação sem senha.", - "SEND_DESCRIPTION": "Envie a si mesmo um link de registro para o seu endereço de e-mail.", - "SEND": "Enviar link de registro", - "SENT": "O e-mail foi entregue com sucesso. Verifique sua caixa de correio para continuar com a configuração.", - "QRCODE_DESCRIPTION": "Gerar um código QR para escanear com outro dispositivo.", - "QRCODE": "Gerar código QR", - "QRCODE_SCAN": "Escanear este código QR para continuar com a configuração no seu dispositivo.", - "NEW_DESCRIPTION": "Use este dispositivo para configurar a autenticação sem senha.", - "NEW": "Adicionar novo" - } - }, - "MFA": { - "TABLETYPE": "Tipo", - "TABLESTATE": "Status", - "NAME": "Nome", - "EMPTY": "Nenhum fator adicional", - "TITLE": "Autenticação Multifator", - "DESCRIPTION": "Adicione um segundo fator para garantir a segurança ideal da sua conta.", - "MANAGE_DESCRIPTION": "Gerencie os métodos de segundo fator dos seus usuários.", - "ADD": "Adicionar Fator", - "OTP": "Aplicativo de Autenticação para OTP (One-Time Password)", - "OTP_DIALOG_TITLE": "Adicionar OTP", - "OTP_DIALOG_DESCRIPTION": "Digitalize o código QR com um aplicativo autenticador e insira o código abaixo para verificar e ativar o método OTP.", - "U2F": "Impressão digital, Chaves de Segurança, Face ID e outros", - "U2F_DIALOG_TITLE": "Verificar Fator", - "U2F_DIALOG_DESCRIPTION": "Digite um nome para o seu fator de multifator universal usado.", - "U2F_SUCCESS": "Fator adicionado com sucesso!", - "U2F_ERROR": "Ocorreu um erro durante a configuração!", - "U2F_NAME": "Nome do Autenticador", - "TYPE": { - "0": "Nenhum MFA definido", - "1": "One Time Password (OTP)", - "2": "Impressão digital, Chaves de Segurança, Face ID e outros" - }, - "STATE": { - "0": "Nenhum Estado", - "1": "Não Pronto", - "2": "Pronto", - "3": "Excluído" - }, - "DIALOG": { - "MFA_DELETE_TITLE": "Remover Segundo Fator", - "MFA_DELETE_DESCRIPTION": "Você está prestes a excluir um segundo fator. Tem certeza?", - "ADD_MFA_TITLE": "Adicionar Segundo Fator", - "ADD_MFA_DESCRIPTION": "Selecione uma das opções disponíveis para criar um método de autenticação de segundo fator." - } - }, - "EXTERNALIDP": { - "TITLE": "Provedores de Identidade Externos", - "DESC": "", - "IDPCONFIGID": "ID da Configuração do Provedor de Identidade", - "IDPNAME": "Nome do Provedor de Identidade", - "USERDISPLAYNAME": "Nome Externo", - "EXTERNALUSERID": "ID do Usuário Externo", - "EMPTY": "Nenhum IDP externo encontrado", - "DIALOG": { - "DELETE_TITLE": "Remover IDP", - "DELETE_DESCRIPTION": "Você está prestes a excluir um Provedor de Identidade de um usuário. Deseja continuar?" - } - }, - "CREATE": { - "TITLE": "Criar um Novo Usuário", - "DESCRIPTION": "Forneça as informações necessárias.", - "NAMEANDEMAILSECTION": "Nome e E-mail", - "GENDERLANGSECTION": "Gênero e Idioma", - "PHONESECTION": "Números de Telefone", - "PASSWORDSECTION": "Senha Inicial", - "ADDRESSANDPHONESECTION": "Número de telefone", - "INITMAILDESCRIPTION": "Se ambas as opções forem selecionadas, nenhum e-mail de inicialização será enviado. Se apenas uma das opções for selecionada, um e-mail para fornecer/verificar os dados será enviado." - }, - "CODEDIALOG": { - "TITLE": "Verificar Número de Telefone", - "DESCRIPTION": "Digite o código que você recebeu por mensagem de texto para verificar seu número de telefone.", - "CODE": "Código" - }, - "DATA": { - "STATE": "Status", - "STATE0": "Desconhecido", - "STATE1": "Ativo", - "STATE2": "Inativo", - "STATE3": "Excluído", - "STATE4": "Bloqueado", - "STATE5": "Suspenso", - "STATE6": "Inicial" - }, - "PROFILE": { - "TITLE": "Perfil", - "EMAIL": "E-mail", - "PHONE": "Número de Telefone", - "PHONE_HINT": "Use 00 ou o símbolo + seguido do código de chamada do país, ou selecione o país na lista suspensa e, em seguida, insira o número de telefone", - "USERNAME": "Nome de Usuário", - "CHANGEUSERNAME": "modificar", - "CHANGEUSERNAME_TITLE": "Alterar nome de usuário", - "CHANGEUSERNAME_DESC": "Digite o novo nome no campo abaixo.", - "FIRSTNAME": "Nome Próprio", - "LASTNAME": "Sobrenome", - "NICKNAME": "Apelido", - "DISPLAYNAME": "Nome de Exibição", - "PREFERRED_LANGUAGE": "Idioma", - "GENDER": "Gênero", - "SENHA": "Senha", - "AVATAR": { - "UPLOADTITLE": "Carregar sua Foto de Perfil", - "UPLOADBTN": "Escolher arquivo", - "UPLOAD": "Carregar", - "CURRENT": "Foto Atual", - "PREVIEW": "Visualização", - "DELETESUCCESS": "Excluído com sucesso!", - "CROPPERERROR": "Ocorreu um erro ao carregar seu arquivo. Tente um formato e tamanho diferentes, se necessário." - }, - "COUNTRY": "País" - }, - "MACHINE": { - "TITLE": "Detalhes do Usuário de Serviço", - "USERNAME": "Nome de Usuário", - "NAME": "Nome", - "DESCRIPTION": "Descrição", - "KEYSTITLE": "Chaves", - "KEYSDESC": "Defina suas chaves e adicione uma data de expiração opcional.", - "TOKENSTITLE": "Tokens de Acesso Pessoal", - "TOKENSDESC": "Os tokens de acesso pessoal funcionam como tokens de acesso OAuth comuns.", - "ID": "ID da Chave", - "TYPE": "Tipo", - "EXPIRATIONDATE": "Data de Expiração", - "CHOOSEDATEAFTER": "Insira uma data de expiração válida após", - "CHOOSEEXPIRY": "Selecione uma data de expiração", - "CREATIONDATE": "Data de Criação", - "KEYDETAILS": "Detalhes da Chave", - "ACCESSTOKENTYPE": "Tipo de Token de Acesso", - "ACCESSTOKENTYPES": { - "0": "Bearer", - "1": "JWT" - }, - "ADD": { - "TITLE": "Adicionar Chave", - "DESCRIPTION": "Selecione o tipo de chave e escolha uma data de expiração opcional." - }, - "ADDED": { - "TITLE": "A chave foi criada", - "DESCRIPTION": "Baixe a chave, pois ela não será mais visível após o fechamento deste diálogo!" - }, - "KEYTYPES": { - "1": "JSON" - }, - "DIALOG": { - "DELETE_KEY": { - "TITLE": "Excluir Chave", - "DESCRIPTION": "Deseja excluir a chave selecionada? Essa ação não pode ser desfeita." - } - } - }, - "PASSWORD": { - "TITLE": "Senha", - "LABEL": "Uma senha segura ajuda a proteger a conta", - "DESCRIPTION": "Digite a nova senha de acordo com a política abaixo.", - "OLD": "Senha Atual", - "NEW": "Nova Senha", - "CONFIRM": "Confirmar Nova Senha", - "NEWINITIAL": "Senha", - "CONFIRMINITIAL": "Confirmar Senha", - "RESET": "Redefinir Senha Atual", - "SET": "Definir Nova Senha", - "RESENDNOTIFICATION": "Enviar Link de Redefinição de Senha", - "REQUIRED": "Algumas informações obrigatórias estão faltando.", - "MINLENGTHERROR": "Deve ter pelo menos {{value}} caracteres." - }, - "ID": "ID", - "EMAIL": "E-mail", - "PHONE": "Número de Telefone", - "PHONEEMPTY": "Nenhum número de telefone definido", - "PHONEVERIFIED": "Número de telefone verificado.", - "EMAILVERIFIED": "E-mail verificado", - "NOTVERIFIED": "não verificado", - "PREFERRED_LOGINNAME": "Nome de Usuário Preferencial", - "ISINITIAL": "O usuário ainda não está ativo.", - "LOGINMETHODS": { - "TITLE": "Informações de Contato", - "DESCRIPTION": "As informações fornecidas serão usadas para enviar informações importantes, como e-mails de redefinição de senha.", - "EMAIL": { - "TITLE": "E-mail", - "VALID": "validado", - "ISVERIFIED": "E-mail Verificado", - "ISVERIFIEDDESC": "Se o e-mail estiver verificado, nenhuma solicitação de verificação será feita.", - "RESEND": "Reenviar E-mail de Verificação", - "EDITTITLE": "Alterar E-mail", - "EDITDESC": "Digite o novo e-mail no campo abaixo." - }, - "PHONE": { - "TITLE": "Telefone", - "VALID": "validado", - "RESEND": "Reenviar Mensagem de Texto de Verificação", - "EDITTITLE": "Alterar Número", - "EDITVALUE": "Número de Telefone", - "EDITDESC": "Digite o novo número de telefone no campo abaixo.", - "DELETETITLE": "Excluir número de telefone", - "DELETEDESC": "Você realmente deseja excluir o número de telefone?" - }, - "RESENDCODE": "Reenviar Código", - "ENTERCODE": "Verificar", - "ENTERCODE_DESC": "Digite o código de verificação" - }, - "GRANTS": { - "TITLE": "Permissões de Usuário", - "DESCRIPTION": "Conceda a este usuário acesso a determinados projetos.", - "CREATE": { - "TITLE": "Criar Permissão de Usuário", - "DESCRIPTION": "Pesquise a organização, o projeto e as funções do projeto correspondentes." - }, - "PROJECTNAME": "Nome do Projeto", - "PROJECT-OWNED": "Projeto", - "PROJECT-GRANTED": "Projeto Concedido", - "FILTER": { - "0": "filtro por usuário", - "1": "filtro por domínio", - "2": "filtro por nome do projeto", - "3": "filtro por nome da função" - } - }, - "STATE": { - "0": "Desconhecido", - "1": "Ativo", - "2": "Inativo", - "3": "Excluído", - "4": "Bloqueado", - "5": "Suspenso", - "6": "Inicial" - }, - "SEARCH": { - "ADDITIONAL": "Nome de usuário (organização atual)", - "ADDITIONAL-EXTERNAL": "Nome de usuário (organização externa)" - }, - "TARGET": { - "SELF": "Se você deseja conceder acesso a um usuário de outra organização", - "EXTERNAL": "Para conceder acesso a um usuário de sua organização", - "CLICKHERE": "clique aqui" - }, - "SIGNEDOUT": "Você saiu. Clique no botão \"Entrar\" para fazer login novamente.", - "SIGNEDOUT_BTN": "Entrar", - "EDITACCOUNT": "Editar Conta", - "ADDACCOUNT": "Entrar Com Outra Conta", - "RESENDINITIALEMAIL": "Reenviar e-mail de ativação", - "RESENDEMAILNOTIFICATION": "Reenviar notificação por e-mail", - "TOAST": { - "CREATED": "Usuário criado com sucesso.", - "SAVED": "Perfil salvo com sucesso.", - "USERNAMECHANGED": "Nome de usuário alterado.", - "EMAILSAVED": "E-mail salvo com sucesso.", - "INITEMAILSENT": "E-mail de ativação enviado.", - "PHONESAVED": "Telefone salvo com sucesso.", - "PHONEREMOVED": "O número de telefone foi removido.", - "PHONEVERIFIED": "Número de telefone verificado com sucesso.", - "PHONEVERIFICATIONSENT": "Código de verificação de telefone enviado.", - "EMAILVERIFICATIONSENT": "Código de verificação de e-mail enviado.", - "OTPREMOVED": "OTP removido.", - "U2FREMOVED": "Fator removido.", - "PASSWORDLESSREMOVED": "Autenticação sem senha removida.", - "INITIALPASSWORDSET": "Senha inicial definida.", - "PASSWORDNOTIFICATIONSENT": "Notificação de alteração de senha enviada.", - "PASSWORDCHANGED": "Senha alterada com sucesso.", - "REACTIVATED": "Usuário reativado.", - "DEACTIVATED": "Usuário desativado.", - "SELECTEDREACTIVATED": "Usuários selecionados reativados.", - "SELECTEDDEACTIVATED": "Usuários selecionados desativados.", - "SELECTEDKEYSDELETED": "Chaves selecionadas excluídas.", - "KEYADDED": "Chave adicionada!", - "MACHINEADDED": "Usuário de serviço criado!", - "DELETED": "Usuário excluído com sucesso!", - "UNLOCKED": "Usuário desbloqueado com sucesso!", - "PASSWORDLESSREGISTRATIONSENT": "Link de registro enviado com sucesso.", - "SECRETGENERATED": "Segredo gerado com sucesso!", - "SECRETREMOVED": "Segredo removido com sucesso!" - }, - "MEMBERSHIPS": { - "TITLE": "Funções de Gerente do ZITADEL", - "DESCRIPTION": "Essas são todas as permissões de membros do usuário. Você também pode modificá-las nas páginas de detalhes da organização, projeto ou IAM.", - "ORGCONTEXT": "Você vê todas as organizações e projetos relacionados à organização atualmente selecionada.", - "USERCONTEXT": "Você vê todas as organizações e projetos para os quais está autorizado. Incluindo outras organizações.", - "CREATIONDATE": "Data de Criação", - "CHANGEDATE": "Última Modificação", - "DISPLAYNAME": "Nome de Exibição", - "REMOVE": "Remover", - "TYPE": "Tipo", - "ORGID": "ID da Organização", - "UPDATED": "A permissão de membro foi atualizada.", - "NOPERMISSIONTOEDIT": "Você não possui as permissões necessárias para editar funções!", - "TYPES": { - "UNKNOWN": "Desconhecido", - "ORG": "Organização", - "PROJECT": "Projeto", - "GRANTEDPROJECT": "Projeto Concedido" - } - }, - "PERSONALACCESSTOKEN": { - "ID": "ID", - "TOKEN": "Token", - "ADD": { - "TITLE": "Gerar Novo Token de Acesso Pessoal", - "DESCRIPTION": "Defina um tempo de expiração personalizado para o token.", - "CHOOSEEXPIRY": "Selecione uma data de expiração", - "CHOOSEDATEAFTER": "Insira uma data de expiração válida após" - }, - "ADDED": { - "TITLE": "Token de Acesso Pessoal", - "DESCRIPTION": "Certifique-se de copiar o seu token de acesso pessoal. Você não poderá vê-lo novamente!" - }, - "DELETE": { - "TITLE": "Excluir Token", - "DESCRIPTION": "Você está prestes a excluir o token de acesso pessoal. Tem certeza?" - }, - "DELETED": "Token excluído com sucesso." - } - }, - "METADATA": { - "TITLE": "Metadados", - "DESCRIPTION": "", - "KEY": "Chave", - "VALUE": "Valor", - "ADD": "Nova Entrada", - "SAVE": "Salvar", - "EMPTY": "Nenhum metadado", - "SETSUCCESS": "Elemento salvo com sucesso", - "REMOVESUCCESS": "Elemento excluído com sucesso" - }, - "FLOWS": { - "TITLE": "Ações e Fluxos", - "DESCRIPTION": "Defina scripts para executar em determinado evento.", - "ACTIONSTITLE": "Ações", - "ACTIONSDESCRIPTION": "Esses são seus scripts que você pode executar nos fluxos.", - "FLOWSTITLE": "Fluxos", - "FLOWSDESCRIPTION": "Fluxos de um determinado tipo executam ações no momento de um gatilho.", - "ID": "ID", - "NAME": "Nome", - "STATE": "Estado", - "STATES": { - "0": "sem status", - "1": "inativo", - "2": "ativo" - }, - "ADDTRIGGER": "Adicionar gatilho", - "FLOWCHANGED": "O fluxo foi alterado com sucesso", - "FLOWCLEARED": "O fluxo foi redefinido com sucesso", - "TIMEOUT": "Tempo Limite", - "TIMEOUTINSEC": "Tempo Limite em segundos", - "ALLOWEDTOFAIL": "Permitido Falhar", - "SCRIPT": "Script", - "FLOWTYPE": "Tipo de Fluxo", - "TRIGGERTYPE": "Tipo de Gatilho", - "ACTIONS": "Ações", - "ACTIONSMAX": "Com base no seu Nível, você tem um número limitado de Ações disponíveis ({{value}}). Certifique-se de desativar as que você não precisa ou considere fazer upgrade do seu nível.", - "DIALOG": { - "ADD": { - "TITLE": "Criar uma Ação" - }, - "UPDATE": { - "TITLE": "Atualizar Ação" - }, - "DELETEACTION": { - "TITLE": "Excluir Ação?", - "DESCRIPTION": "Você está prestes a excluir uma ação. Isso não pode ser desfeito. Tem certeza?", - "DELETE_SUCCESS": "Ação excluída com sucesso." - }, - "CLEAR": { - "TITLE": "Limpar fluxo?", - "DESCRIPTION": "Você está prestes a redefinir o fluxo juntamente com seus gatilhos e ações. Essa alteração não pode ser desfeita. Tem certeza?" - }, - "REMOVEACTIONSLIST": { - "TITLE": "Excluir Ações Selecionadas?", - "DESCRIPTION": "Tem certeza de que deseja excluir as ações selecionadas do fluxo?" - } - }, - "TOAST": { - "ACTIONSSET": "Ações definidas", - "ACTIONREACTIVATED": "Ações reativadas com sucesso", - "ACTIONDEACTIVATED": "Ações desativadas com sucesso" - } - }, - "IAM": { - "TITLE": "Instância", - "DESCRIPTION": "Gerencie as configurações da sua instância e organizações", - "POLICIES": { - "TITLE": "Políticas do sistema e configurações de acesso", - "DESCRIPTION": "Gerencie as políticas globais e configurações de acesso à gerência." - }, - "EVENTSTORE": { - "TITLE": "Administração de armazenamento IAM", - "DESCRIPTION": "Gerencie as visualizações do ZITADEL e eventos falhados." - }, - "MEMBER": { - "TITLE": "Gerentes", - "DESCRIPTION": "Esses gerentes têm permissão para fazer alterações na sua instância." - }, - "PAGES": { - "STATE": "Status", - "DOMAINLIST": "Domínios" - }, - "STATE": { - "0": "Não especificado", - "1": "Criando", - "2": "Executando", - "3": "Parando", - "4": "Parado" - }, - "VIEWS": { - "TITLE": "Visualizações", - "DESCRIPTION": "Este cartão mostra as visualizações do ZITADEL.", - "VIEWNAME": "Nome", - "DATABASE": "Banco de Dados", - "SEQUENCE": "Sequência", - "EVENTTIMESTAMP": "Carimbo de Data/Hora do Evento", - "LASTSPOOL": "Spool bem-sucedido", - "ACTIONS": "Ações", - "CLEAR": "Limpar", - "CLEARED": "Visualização limpa com sucesso!", - "DIALOG": { - "VIEW_CLEAR_TITLE": "Limpar Visualização", - "VIEW_CLEAR_DESCRIPTION": "Você está prestes a limpar uma visualização. A limpeza de uma visualização cria um processo durante o qual os dados possivelmente não estarão disponíveis para os usuários finais. Você tem certeza?" - } - }, - "FAILEDEVENTS": { - "TITLE": "Eventos com falha", - "DESCRIPTION": "Este cartão mostra os eventos com falha.", - "VIEWNAME": "Nome", - "DATABASE": "Banco de Dados", - "FAILEDSEQUENCE": "Sequência com falha", - "FAILURECOUNT": "Contagem de falhas", - "LASTFAILED": "Última falha em", - "ERRORMESSAGE": "Mensagem de erro", - "ACTIONS": "Ações", - "DELETE": "Remover", - "DELETESUCCESS": "Eventos com falha removidos." - }, - "EVENTS": { - "TITLE": "Eventos", - "DESCRIPTION": "Esta visualização mostra todos os eventos ocorridos.", - "EDITOR": "Editor", - "EDITORID": "ID do Editor", - "AGGREGATE": "Agregado", - "AGGREGATEID": "ID do Agregado", - "AGGREGATETYPE": "Tipo do Agregado", - "RESOURCEOWNER": "Proprietário do Recurso", - "SEQUENCE": "Sequência", - "CREATIONDATE": "Criado em", - "TYPE": "Tipo", - "PAYLOAD": "Carga", - "FILTERS": { - "BTN": "Filtrar", - "USER": { - "IDLABEL": "ID", - "CHECKBOX": "Filtrar por Editor" - }, - "AGGREGATE": { - "TYPELABEL": "Tipo de Agregado", - "IDLABEL": "ID", - "CHECKBOX": "Filtrar por Agregado" - }, - "TYPE": { - "TYPELABEL": "Tipo", - "CHECKBOX": "Filtrar por Tipo" - }, - "RESOURCEOWNER": { - "LABEL": "ID", - "CHECKBOX": "Filtrar por Proprietário do Recurso" - }, - "SEQUENCE": { - "LABEL": "Sequência", - "CHECKBOX": "Filtrar por Sequência", - "SORT": "Ordenação", - "ASC": "Crescente", - "DESC": "Decrescente" - }, - "CREATIONDATE": { - "LABEL": "Data de Criação", - "CHECKBOX": "Filtrar por Data de Criação" - }, - "OTHER": "outro", - "OTHERS": "outros" - }, - "DIALOG": { - "TITLE": "Detalhes do Evento" - } - }, - "TOAST": { - "MEMBERREMOVED": "Gerente removido.", - "MEMBERSADDED": "Gerentes adicionados.", - "MEMBERADDED": "Gerente adicionado.", - "MEMBERCHANGED": "Gerente alterado.", - "ROLEREMOVED": "Função removida.", - "ROLECHANGED": "Função alterada.", - "REACTIVATED": "Reativado", - "DEACTIVATED": "Desativado" - } - }, - "ORG": { - "PAGES": { - "NAME": "Nome", - "ID": "ID", - "CREATIONDATE": "Data de Criação", - "DATECHANGED": "Alterado", - "FILTER": "Filtro", - "FILTERPLACEHOLDER": "Filtrar pelo nome", - "LIST": "Organizações", - "LISTDESCRIPTION": "Escolha uma organização.", - "ACTIVE": "Ativa", - "CREATE": "Criar Organização", - "DEACTIVATE": "Desativar Organização", - "REACTIVATE": "Reativar Organização", - "NOPERMISSION": "Você não tem permissão para acessar as configurações da organização.", - "USERSELFACCOUNT": "Use sua conta pessoal como proprietário da organização", - "ORGDETAIL_TITLE": "Digite o nome e domínio da sua nova organização.", - "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Digite o nome da sua nova organização.", - "ORGDETAILUSER_TITLE": "Configurar Proprietário da Organização", - "DELETE": "Excluir organização", - "DEFAULTLABEL": "Padrão", - "SETASDEFAULT": "Definir como organização padrão", - "DEFAULTORGSET": "Organização padrão alterada com sucesso", - "RENAME": { - "ACTION": "Renomear", - "TITLE": "Renomear Organização", - "DESCRIPTION": "Digite o novo nome para sua organização", - "BTN": "Renomear" - }, - "ORGDOMAIN": { - "TITLE": "Verificação de Propriedade do Domínio da Organização", - "VERIFICATION": "Para verificar a propriedade do seu domínio, você precisa baixar um arquivo de verificação e enviá-lo para a URL fornecida abaixo, ou criar um registro DNS TXT para a URL fornecida. Para concluir, clique no botão para verificar.", - "VERIFICATION_SKIP": "Você pode pular a verificação por enquanto e continuar a criar sua organização, mas para usar sua organização, esta etapa deve ser concluída!", - "VERIFICATION_VALIDATION_DESC": "Os tokens são verificados regularmente para garantir que você ainda seja o proprietário do domínio.", - "VERIFICATION_NEWTOKEN_TITLE": "Solicitar Novo Token", - "VERIFICATION_NEWTOKEN_DESC": "Se você deseja solicitar um novo token, selecione seu método preferido. Se você deseja validar um token persistente, clique no botão acima.", - "VERIFICATION_VALIDATION_ONGOING": "Um token de verificação já foi solicitado. Clique no botão para iniciar uma verificação.", - "VERIFICATION_VALIDATION_ONGOING_TYPE": "Tipo do token:", - "VERIFICATION_SUCCESSFUL": "Domínio verificado com sucesso!", - "REQUESTNEWTOKEN": "Solicitar novo token", - "TYPES": { - "1": "HTTP", - "2": "DNS" - } - }, - "DOWNLOAD_FILE": "Baixar Arquivo", - "SELECTORGTOOLTIP": "Selecionar esta organização.", - "PRIMARYDOMAIN": "Domínio Principal", - "STATE": "Estado", - "USEPASSWORD": "Definir Senha Inicial", - "USEPASSWORDDESC": "O usuário não precisa definir a senha durante a inicialização." - }, - "LIST": { - "TITLE": "Organizações", - "DESCRIPTION": "Estas são as organizações na sua instância" - }, - "DOMAINS": { - "NEW": "Adicionar Domínio", - "TITLE": "Domínios", - "DESCRIPTION": "Configure seus domínios. Este domínio pode ser usado para o login dos seus usuários.", - "SETPRIMARY": "Definir como Principal", - "DELETE": { - "TITLE": "Excluir Domínio", - "DESCRIPTION": "Você está prestes a excluir um dos seus domínios. Observe que seus usuários não poderão mais usar este domínio para fazer login." - }, - "ADD": { - "TITLE": "Adicionar Domínio", - "DESCRIPTION": "Você está prestes a adicionar um domínio para sua organização. Após o processo bem-sucedido, seus usuários poderão usar o domínio para fazer login." - } - }, - "STATE": { - "0": "Não definido", - "1": "Ativa", - "2": "Desativada", - "3": "Removida" - }, - "MEMBER": { - "TITLE": "Gerentes da Organização", - "DESCRIPTION": "Defina os usuários que podem alterar as preferências da sua organização." - }, - "TOAST": { - "UPDATED": "Organização atualizada com sucesso.", - "DEACTIVATED": "Organização desativada.", - "REACTIVATED": "Organização reativada.", - "DOMAINADDED": "Domínio adicionado.", - "DOMAINREMOVED": "Domínio removido.", - "MEMBERADDED": "Gerente adicionado.", - "MEMBERREMOVED": "Gerente removido.", - "MEMBERCHANGED": "Gerente alterado.", - "SETPRIMARY": "Domínio principal definido.", - "DELETED": "Organização excluída com sucesso", - "ORG_WAS_DELETED": "Organização foi excluída." - }, - "DIALOG": { - "DEACTIVATE": { - "TITLE": "Desativar organização", - "DESCRIPTION": "Você está prestes a desativar sua organização. Os usuários não poderão fazer login posteriormente. Tem certeza de que deseja continuar?" - }, - "REACTIVATE": { - "TITLE": "Reativar organização", - "DESCRIPTION": "Você está prestes a reativar sua organização. Os usuários poderão fazer login novamente. Tem certeza de que deseja continuar?" - }, - "DELETE": { - "TITLE": "Excluir organização", - "DESCRIPTION": "Você está prestes a excluir sua organização. Isso inicia um processo no qual todos os dados relacionados à organização serão excluídos. Não será possível reverter esta ação por enquanto.", - "TYPENAME": "Digite '{{value}}' para excluir sua organização.", - "ORGNAME": "Nome", - "BTN": "Excluir" - } - } - }, - "SETTINGS": { - "INSTANCE": { - "TITLE": "Configurações da Instância", - "DESCRIPTION": "Essas configurações serão aplicadas a todas as suas organizações, a menos que tenham sido substituídas." - }, - "ORG": { - "TITLE": "Configurações da Organização", - "DESCRIPTION": "Essas configurações estendem e sobrescrevem as configurações da sua instância." - }, - "LIST": { - "GENERAL": "Geral", - "LOGIN": "Comportamento de Login e Segurança", - "LOCKOUT": "Bloqueio", - "COMPLEXITY": "Complexidade de Senha", - "NOTIFICATIONS": "Configurações de Notificação", - "NOTIFICATIONS_DESC": "Configurações de SMTP e SMS", - "MESSAGETEXTS": "Textos de Mensagem", - "IDP": "Provedores de Identidade", - "DOMAIN": "Configurações de Domínio", - "LOGINTEXTS": "Textos da Interface de Login", - "BRANDING": "Marca", - "PRIVACYPOLICY": "Política de Privacidade", - "OIDC": "Tempo de Vida e Expiração do Token OIDC", - "SECRETS": "Aparência de Segredo", - "SECURITY": "Configurações de Segurança" - }, - "GROUPS": { - "NOTIFICATIONS": "Notificações", - "LOGIN": "Login e Acesso", - "DOMAIN": "Domínio", - "TEXTS": "Textos e Idiomas", - "APPEARANCE": "Aparência", - "OTHER": "Outro" - } - }, - "SETTING": { - "DEFAULTLANGUAGE": "Idioma padrão", - "LANGUAGE": { - "de": "Deutsch", - "en": "English", - "es": "Español", - "fr": "Français", - "it": "Italiano", - "ja": "日本語", - "pl": "Polski", - "zh": "简体中文", - "bg": "Български", - "pt": "Portuguese", - "mk": "Македонски" - }, - "SMTP": { - "TITLE": "Configurações SMTP", - "SENDERADDRESS": "Endereço de e-mail do remetente", - "SENDERNAME": "Nome do remetente", - "HOSTANDPORT": "Host e porta", - "USER": "Usuário", - "PASSWORD": "Senha", - "SETPASSWORD": "Definir senha do SMTP", - "PASSWORDSET": "Senha do SMTP definida com sucesso.", - "TLS": "Transport Layer Security (TLS)", - "SAVED": "Salvo com sucesso!", - "REQUIREDWARN": "Para enviar notificações do seu domínio, você precisa inserir seus dados SMTP." - }, - "SMS": { - "TITLE": "Configurações de SMS", - "PROVIDERS": "Provedores", - "PROVIDER": "Provedor de SMS", - "ADDPROVIDER": "Adicionar provedor de SMS", - "ADDPROVIDERDESCRIPTION": "Escolha um dos provedores disponíveis e insira os dados necessários.", - "REMOVEPROVIDER": "Remover provedor", - "REMOVEPROVIDER_DESC": "Você está prestes a excluir uma configuração de provedor. Deseja continuar?", - "SMSPROVIDERSTATE": { - "0": "Não especificado", - "1": "Ativo", - "2": "Inativo" - }, - "ACTIVATED": "Provedor ativado.", - "DEACTIVATED": "Provedor desativado.", - "TWILIO": { - "SID": "SID", - "TOKEN": "Token", - "SENDERNUMBER": "Número do remetente", - "ADDED": "Twilio adicionado com sucesso.", - "REMOVED": "Twilio removido", - "CHANGETOKEN": "Alterar token", - "SETTOKEN": "Definir token", - "TOKENSET": "Token definido com sucesso." - } - }, - "OIDC": { - "TITLE": "Configurações OIDC", - "ACCESSTOKENLIFETIME": "Tempo de vida do token de acesso", - "IDTOKENLIFETIME": "Tempo de vida do token de ID", - "REFRESHTOKENEXPIRATION": "Expiração do token de atualização", - "REFRESHTOKENIDLEEXPIRATION": "Expiração inativa do token de atualização", - "INHOURS": "horas", - "INDAYS": "dias" - }, - "SECRETS": { - "TITLE": "Aparência de segredos", - "TYPES": "Tipos de segredos", - "TYPE": { - "1": "Email de inicialização", - "2": "Verificação de email", - "3": "Verificação de telefone", - "4": "Redefinição de senha", - "5": "Inicialização sem senha", - "6": "Segredo do aplicativo" - }, - "ADDGENERATOR": "Definir aparência de segredo", - "GENERATORTYPE": "Tipo", - "EXPIRY": "Expiração (em horas)", - "INCLUDEDIGITS": "Incluir números", - "INCLUDESYMBOLS": "Incluir símbolos", - "INCLUDELOWERLETTERS": "Incluir letras minúsculas", - "INCLUDEUPPERLETTERS": "Incluir letras maiúsculas", - "LENGTH": "Comprimento", - "UPDATED": "Configurações atualizadas." - }, - "SECURITY": { - "DESCRIPTION": "Essa configuração define o CSP para permitir o enquadramento de um conjunto de domínios permitidos. Observe que, ao permitir o uso de iFrames, você corre o risco de permitir ataques de clickjacking.", - "IFRAMEENABLED": "Permitir iFrame", - "ALLOWEDORIGINS": "URLs permitidos" - }, - "DIALOG": { - "RESET": { - "DEFAULTTITLE": "Redefinir configuração", - "DEFAULTDESCRIPTION": "Você está prestes a redefinir suas configurações para a configuração padrão da sua instância. Tem certeza de que deseja continuar?", - "LOGINPOLICY_DESCRIPTION": "Aviso: Se você continuar, as configurações do provedor de identidade também serão redefinidas para as configurações da instância." - } - } - }, - "POLICY": { - "TITLE": "Explorar configurações", - "DESCRIPTION": "Configurações pré-embaladas que melhoram sua segurança.", - "APPLIEDTO": "Aplicado a", - "PWD_COMPLEXITY": { - "TITLE": "Complexidade da senha", - "DESCRIPTION": "Garante que todas as senhas definidas correspondam a um padrão específico", - "SYMBOLANDNUMBERERROR": "Deve conter um dígito e um símbolo/ponto de pontuação.", - "SYMBOLERROR": "Deve incluir um símbolo/ponto de pontuação.", - "NUMBERERROR": "Deve incluir um dígito.", - "PATTERNERROR": "A senha não atende ao padrão exigido." - }, - "NOTIFICATION": { - "TITLE": "Notificação", - "DESCRIPTION": "Determina em quais alterações as notificações serão enviadas.", - "PASSWORDCHANGE": "Mudança de senha" - }, - "PRIVATELABELING": { - "TITLE": "Marca", - "DESCRIPTION": "Dê ao login o seu estilo personalizado e modifique seu comportamento.", - "PREVIEW_DESCRIPTION": "As alterações da política serão automaticamente aplicadas ao ambiente de visualização.", - "BTN": "Selecionar arquivo", - "ACTIVATEPREVIEW": "Aplicar configuração", - "DARK": "Modo escuro", - "LIGHT": "Modo claro", - "CHANGEVIEW": "Alterar visualização", - "ACTIVATED": "As alterações de política estão agora ATIVAS", - "THEME": "Tema", - "COLORS": "Cores", - "FONT": "Fonte", - "ADVANCEDBEHAVIOR": "Comportamento avançado", - "DROP": "Solte a imagem aqui ou", - "RELEASE": "Liberar", - "DROPFONT": "Solte o arquivo de fonte aqui", - "RELEASEFONT": "Liberar", - "USEOFLOGO": "Seu logotipo será usado no login e nos emails, enquanto o ícone é usado para elementos de IU menores, como o seletor de organização no console", - "MAXSIZE": "O tamanho máximo é limitado a 524kB", - "EMAILNOSVG": "O formato de arquivo SVG não é suportado em emails. Portanto, faça o upload do seu logotipo em PNG ou em outro formato suportado.", - "MAXSIZEEXCEEDED": "Tamanho máximo de 524kB excedido.", - "NOSVGSUPPORTED": "SVG não é suportado!", - "FONTINLOGINONLY": "A fonte está atualmente sendo exibida apenas na interface de login.", - "BACKGROUNDCOLOR": "Cor de fundo", - "PRIMARYCOLOR": "Cor primária", - "WARNCOLOR": "Cor de aviso", - "FONTCOLOR": "Cor da fonte", - "VIEWS": { - "PREVIEW": "Visualização", - "CURRENT": "Configuração atual" - }, - "PREVIEW": { - "TITLE": "Login", - "SECOND": "faça login com sua conta ZITADEL.", - "ERROR": "Usuário não encontrado!", - "PRIMARYBUTTON": "próximo", - "SECONDARYBUTTON": "registrar" - } - }, - "PWD_AGE": { - "TITLE": "Envelhecimento de senha", - "DESCRIPTION": "Você pode definir uma política para o envelhecimento de senhas. Essa política emite um aviso após o tempo de envelhecimento específico ter passado." - }, - "PWD_LOCKOUT": { - "TITLE": "Política de bloqueio", - "DESCRIPTION": "Defina um número máximo de tentativas de senha, após as quais as contas serão bloqueadas." - }, - "DOMAIN_POLICY": { - "TITLE": "Configurações de domínio" - }, - "PRIVATELABELING_POLICY": { - "TITLE": "Marca", - "BTN": "Selecionar arquivo", - "DESCRIPTION": "Personalize a aparência do login", - "ACTIVATEPREVIEW": "Ativar configuração" - }, - "LOGIN_POLICY": { - "TITLE": "Configurações de login", - "DESCRIPTION": "Defina como os usuários podem ser autenticados e configure provedores de identidade", - "DESCRIPTIONCREATEADMIN": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo.", - "DESCRIPTIONCREATEMGMT": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo. Observação: você também pode usar provedores definidos pelo sistema e provedores definidos apenas para sua organização.", - "ADVANCED": "Avançado", - "LIFETIMEDURATIONS": "Duração do login", - "SAVED": "Salvo com sucesso!" - }, - "PRIVACY_POLICY": { - "TITLE": "Política de privacidade e TOS", - "DESCRIPTION": "Defina os links para a sua Política de Privacidade e Termos de Serviço", - "TOSLINK": "Link para os Termos de Serviço", - "POLICYLINK": "Link para a Política de Privacidade", - "HELPLINK": "Link para Ajuda", - "SUPPORTEMAIL": "E-mail de suporte", - "SAVED": "Salvo com sucesso!", - "RESET_TITLE": "Restaurar valores padrão", - "RESET_DESCRIPTION": "Você está prestes a restaurar os Links padrão para TOS e Política de Privacidade. Deseja realmente continuar?" - }, - "LOGIN_TEXTS": { - "TITLE": "Textos da interface de login", - "DESCRIPTION": "Defina seus textos para as interfaces de login. Se os textos estiverem vazios, o valor padrão mostrado como marcador de posição será usado.", - "DESCRIPTION_SHORT": "Defina seus textos para as interfaces de login.", - "NEWERVERSIONEXISTS": "Existe uma versão mais recente", - "CURRENTDATE": "Configuração atual", - "CHANGEDATE": "Nova versão a partir de", - "KEYNAME": "Tela/Interface de login", - "RESET_TITLE": "Restaurar valores padrão", - "RESET_DESCRIPTION": "Você está prestes a restaurar todos os valores padrão. Todas as alterações que você fez serão excluídas permanentemente. Deseja realmente continuar?", - "UNSAVED_TITLE": "Continuar sem salvar?", - "UNSAVED_DESCRIPTION": "Você fez alterações sem salvar. Deseja salvar agora?", - "LOCALE": "Código de localidade", - "LOCALES": { - "de": "Deutsch", - "en": "English", - "es": "Español", - "fr": "Français", - "it": "Italiano", - "ja": "日本語", - "pl": "Polski", - "zh": "简体中文", - "bg": "Български", - "pt": "Portuguese", - "mk": "Македонски" - }, - "KEYS": { - "emailVerificationDoneText": "Verificação de email concluída", - "emailVerificationText": "Verificação de email", - "externalUserNotFoundText": "Usuário externo não encontrado", - "footerText": "Rodapé", - "initMfaDoneText": "Inicialização de MFA concluída", - "initMfaOtpText": "Inicialização de MFA", - "initMfaPromptText": "Prompt de inicialização de MFA", - "initMfaU2fText": "Inicialização de Fator de Segundo Universal", - "initPasswordDoneText": "Inicialização de senha concluída", - "initPasswordText": "Inicialização de senha", - "initializeDoneText": "Inicialização de usuário concluída", - "initializeUserText": "Inicializaçãode usuário", - "linkingUserDoneText": "Vinculação de usuário concluída", - "loginText": "Login", - "logoutText": "Logout", - "mfaProvidersText": "Provedores de MFA", - "passwordChangeDoneText": "Alteração de senha concluída", - "passwordChangeText": "Alteração de senha", - "passwordResetDoneText": "Redefinição de senha concluída", - "passwordText": "Senha", - "registrationOptionText": "Opções de registro", - "registrationOrgText": "Registrar organização", - "registrationUserText": "Registrar usuário", - "selectAccountText": "Selecionar conta", - "successLoginText": "Login com sucesso", - "usernameChangeDoneText": "Alteração de nome de usuário concluída", - "usernameChangeText": "Alteração de nome de usuário", - "verifyMfaOtpText": "Verificar OTP", - "verifyMfaU2fText": "Verificar Fator de Segundo Universal", - "passwordlessPromptText": "Prompt de autenticação sem senha", - "passwordlessRegistrationDoneText": "Registro sem senha concluído", - "passwordlessRegistrationText": "Registro sem senha", - "passwordlessText": "Sem senha", - "externalRegistrationUserOverviewText": "Visão geral do usuário de registro externo" - } - }, - "MESSAGE_TEXTS": { - "TITLE": "Textos de mensagem", - "DESCRIPTION": "Defina seus textos para os emails de notificação.", - "TYPE": "Notificação", - "TYPES": { - "INIT": "Inicialização", - "VE": "Verificar Email", - "VP": "Verificar Telefone", - "PR": "Redefinição de Senha", - "DC": "Reivindicação de Domínio", - "PL": "Sem senha", - "PC": "Alteração de Senha" - }, - "CHIPS": { - "firstname": "Nome próprio", - "lastname": "Sobrenome", - "code": "Código", - "preferredLoginName": "Nome de login preferido", - "displayName": "Nome de exibição", - "nickName": "Apelido", - "loginnames": "Nomes de login", - "domain": "Domínio", - "lastEmail": "Último email", - "lastPhone": "Último telefone", - "verifiedEmail": "Email verificado", - "verifiedPhone": "Telefone verificado", - "changedate": "Data de alteração", - "username": "Nome de usuário", - "tempUsername": "Nome de usuário temporário" - }, - "TOAST": { - "UPDATED": "Textos personalizados salvos." - } - }, - "DEFAULTLABEL": "As configurações atuais correspondem ao padrão de sua Instância.", - "BTN_INSTALL": "Configurar", - "BTN_EDIT": "Modificar", - "DATA": { - "DESCRIPTION": "Descrição", - "MINLENGTH": "comprimento mínimo", - "HASNUMBER": "tem número", - "HASSYMBOL": "tem símbolo", - "HASLOWERCASE": "tem letra minúscula", - "HASUPPERCASE": "tem letra maiúscula", - "SHOWLOCKOUTFAILURES": "mostrar falhas de bloqueio", - "MAXATTEMPTS": "Máximo de tentativas de senha", - "EXPIREWARNDAYS": "Aviso de expiração após dias", - "MAXAGEDAYS": "Idade máxima em dias", - "USERLOGINMUSTBEDOMAIN": "Adicionar domínio da organização como sufixo aos nomes de login", - "USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Se você habilitar essa configuração, todos os nomes de login serão sufixados com o domínio da organização. Se essa configuração estiver desabilitada, você deve garantir que os nomes de usuário sejam exclusivos em todas as organizações.", - "VALIDATEORGDOMAINS": "Validar domínios da organização", - "SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "O endereço do remetente do SMTP corresponde ao domínio da Instância", - "ALLOWUSERNAMEPASSWORD": "Permitir usuário e senha", - "ALLOWEXTERNALIDP": "Permitir provedor de ID externo", - "ALLOWREGISTER": "Permitir registro", - "ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.", - "ALLOWEXTERNALIDP_DESC": "O login é permitido para os provedores de identidade subjacentes", - "ALLOWREGISTER_DESC": "Se a opção estiver selecionada, uma etapa adicional para registrar um usuário aparecerá no login.", - "FORCEMFA": "Forçar MFA", - "FORCEMFA_DESC": "Se a opção estiver selecionada, os usuários deverão configurar um segundo fator para o login.", - "HIDEPASSWORDRESET": "Ocultar redefinição de senha", - "HIDEPASSWORDRESET_DESC": "Se a opção estiver selecionada, o usuário não poderá redefinir sua senha no processo de login.", - "HIDELOGINNAMESUFFIX": "Ocultar sufixo do nome de login", - "HIDELOGINNAMESUFFIX_DESC": "Oculta o sufixo do nome de login na interface de login", - "IGNOREUNKNOWNUSERNAMES": "Ignorar nomes de usuário desconhecidos", - "IGNOREUNKNOWNUSERNAMES_DESC": "Se a opção estiver selecionada, a tela de senha será exibida no processo de login, mesmo se o usuário não for encontrado. O erro na verificação de senha não revelará se o nome de usuário ou a senha estavam incorretos.", - "ALLOWDOMAINDISCOVERY": "Permitir descoberta de domínio", - "ALLOWDOMAINDISCOVERY_DESC": "Se a opção estiver selecionada, o sufixo (@domain.com) de um nome de usuário desconhecido inserido na tela de login será comparado com os domínios da organização e será redirecionado para o registro dessa organização em caso de sucesso.", - "DISABLELOGINWITHEMAIL": "Desativar login com endereço de email", - "DISABLELOGINWITHPHONE": "Desativar login com número de telefone", - "DEFAULTREDIRECTURI": "URI de redirecionamento padrão", - "DEFAULTREDIRECTURI_DESC": "Define para onde o usuário será redirecionado se o login for iniciado sem um contexto de aplicativo (por exemplo, a partir do e-mail)", - "ERRORMSGPOPUP": "Mostrar Erro em Diálogo", - "DISABLEWATERMARK": "Ocultar Marca d'água", - "DISABLEWATERMARK_DESC": "Ocultar a marca d'água Powered by ZITADEL na interface de login", - "PASSWORDCHECKLIFETIME": "Vida Útil da Verificação de Senha", - "EXTERNALLOGINCHECKLIFETIME": "Vida Útil da Verificação de Login Externo", - "MFAINITSKIPLIFETIME": "Vida Útil da Inicialização de Multifator", - "SECONDFACTORCHECKLIFETIME": "Vida Útil da Verificação de Segundo Fator", - "MULTIFACTORCHECKLIFETIME": "Vida Útil da Verificação de Multifator", - "INHOURS": "horas" - }, - "RESET": "Redefinir para o padrão da instância", - "CREATECUSTOM": "Criar Política Personalizada", - "TOAST": { - "SET": "Política definida com sucesso!", - "RESETSUCCESS": "Política redefinida com sucesso!", - "UPLOADSUCCESS": "Enviado com sucesso!", - "DELETESUCCESS": "Excluído com sucesso!", - "UPLOADFAILED": "Falha no envio!" - } - }, - "ORG_DETAIL": { - "TITLE": "Organização", - "DESCRIPTION": "Aqui você pode editar a configuração da sua organização e gerenciar os membros.", - "DETAIL": { - "TITLE": "Detalhes", - "NAME": "Nome", - "DOMAIN": "Domínio", - "STATE": { - "0": "Não definido", - "1": "Ativo", - "2": "Inativo" - } - }, - "MEMBER": { - "TITLE": "Membros", - "USERNAME": "Nome de Usuário", - "DISPLAYNAME": "Nome de Exibição", - "LOGINNAME": "Nome de Login", - "EMAIL": "E-mail", - "ROLES": "Funções", - "ADD": "Adicionar Membro", - "ADDDESCRIPTION": "Insira os nomes dos usuários a serem adicionados." - }, - "TABLE": { - "TOTAL": "Total de Entradas", - "SELECTION": "Elementos Selecionados", - "DEACTIVATE": "Desativar Usuário", - "ACTIVATE": "Ativar Usuário", - "DELETE": "Excluir Usuário", - "CLEAR": "Limpar seleção" - } - }, - "PROJECT": { - "PAGES": { - "TITLE": "Projeto", - "DESCRIPTION": "Aqui você pode definir aplicativos, gerenciar funções e conceder acesso a outras organizações para usar o seu projeto.", - "DELETE": "Excluir Projeto", - "LIST": "Projetos", - "LISTDESCRIPTION": "Se você não consegue encontrar um projeto, entre em contato com o proprietário do projeto ou alguém com os direitos correspondentes para obter acesso ao projeto.", - "DETAIL": "Detalhes", - "CREATE": "Criar Projeto", - "CREATE_DESC": "Insira o nome do seu projeto.", - "ROLE": "Função", - "NOITEMS": "Nenhum projeto", - "ZITADELPROJECT": "Isto pertence ao projeto ZITADEL. Atenção: Se você fizer alterações, o ZITADEL pode não funcionar como o esperado.", - "TYPE": { - "OWNED": "Projetos Próprios", - "GRANTED": "Projetos Concedidos", - "OWNED_SINGULAR": "Projeto Próprio", - "GRANTED_SINGULAR": "Projeto Concedido" - }, - "PRIVATELABEL": { - "TITLE": "Configuração de Marca", - "0": { - "TITLE": "Não Especificado", - "DESC": "Assim que o usuário for identificado, a marca da organização do usuário identificado será exibida antes da marca padrão do sistema." - }, - "1": { - "TITLE": "Usar configuração do projeto", - "DESC": "Será exibida a marca da organização que possui o projeto" - }, - "2": { - "TITLE": "Usar configuração da organização do usuário", - "DESC": "Será exibida a marca da organização do projeto, mas assim que o usuário for identificado, será exibida a configuração da organização do usuário identificado." - }, - "DIALOG": { - "TITLE": "Configuração de Marca", - "DESCRIPTION": "Selecione o comportamento do login ao usar o projeto." - } - }, - "PINNED": "Fixado", - "ALL": "Todos", - "CREATEDON": "Criado em", - "LASTMODIFIED": "Última modificação em", - "ADDNEW": "Criar Novo Projeto", - "DIALOG": { - "REACTIVATE": { - "TITLE": "Reativar Projeto", - "DESCRIPTION": "Tem certeza de que deseja reativar o seu projeto?" - }, - "DEACTIVATE": { - "TITLE": "Desativar Projeto", - "DESCRIPTION": "Tem certeza de que deseja desativar o seu projeto?" - }, - "DELETE": { - "TITLE": "Excluir Projeto", - "DESCRIPTION": "Tem certeza de que deseja excluir o seu projeto?", - "TYPENAME": "Digite o nome do projeto para excluí-lo permanentemente." - } - } - }, - "SETTINGS": { - "TITLE": "Configurações", - "DESCRIPTION": "" - }, - "STATE": { - "TITLE": "Status", - "0": "Não definido", - "1": "Ativo", - "2": "Inativo" - }, - "TYPE": { - "TITLE": "Tipo", - "0": "Tipo desconhecido", - "1": "Próprio", - "2": "Concedido" - }, - "NAME": "Nome", - "NAMEDIALOG": { - "TITLE": "Renomear Projeto", - "DESCRIPTION": "Digite o novo nome para o seu projeto", - "NAME": "Novo Nome" - }, - "MEMBER": { - "TITLE": "Gerentes", - "TITLEDESC": "Os gerentes podem fazer alterações neste projeto com base em sua função.", - "DESCRIPTION": "Esses gerentes podem ter permissão para editar o seu projeto.", - "USERNAME": "Nome de Usuário", - "DISPLAYNAME": "Nome de Exibição", - "LOGINNAME": "Nome de Login", - "EMAIL": "E-mail", - "ROLES": "Funções", - "USERID": "ID do Usuário" - }, - "GRANT": { - "EMPTY": "Nenhuma organização concedida.", - "TITLE": "Concessões de Projeto", - "DESCRIPTION": "Permite que outra organização use o seu projeto.", - "EDITTITLE": "Editar funções", - "CREATE": { - "TITLE": "Criar Concessão de Organização", - "SEL_USERS": "Selecione os usuários aos quais deseja conceder acesso", - "SEL_PROJECT": "Buscar um projeto", - "SEL_ROLES": "Selecione as funções que deseja adicionar à concessão", - "SEL_USER": "Selecione usuários", - "SEL_ORG": "Definir o domínio", - "SEL_ORG_DESC": "Insira o domínio completo para especificar a organização a ser concedida.", - "ORG_TITLE": "Organização", - "ORG_DESCRIPTION": "Você está prestes a conceder um usuário para a organização {{name}}.", - "ORG_DESCRIPTION_DESC": "Altere o contexto no cabeçalho acima para conceder um usuário para outra organização.", - "SEL_ORG_FORMFIELD": "Domínio Completo", - "SEL_ORG_BUTTON": "Buscar Organização", - "FOR_ORG": "A concessão é criada para:" - }, - "DETAIL": { - "TITLE": "Concessão de Projeto", - "DESC": "Você pode selecionar quais funções podem ser usadas pela organização especificada e eleger gerentes", - "MEMBERTITLE": "Gerentes", - "MEMBERDESC": "Estes são os gerentes da organização concedida. Adicione usuários aqui que devem ter acesso para editar os dados do projeto.", - "PROJECTNAME": "Nome do Projeto", - "GRANTEDORG": "Organização Concedida", - "RESOURCEOWNER": "Proprietário do Recurso" - }, - "STATE": "Status", - "STATES": { - "1": "Ativo", - "2": "Inativo" - }, - "ALL": "Todos", - "SHOWDETAIL": "Mostrar Detalhes", - "USER": "Usuário", - "MEMBERS": "Gerentes", - "ORG": "Organização", - "PROJECTNAME": "Nome do Projeto", - "GRANTEDORG": "Organização Concedida", - "GRANTEDORGDOMAIN": "Domínio", - "RESOURCEOWNER": "Proprietário do Recurso", - "GRANTEDORGNAME": "Nome da Organização", - "GRANTID": "ID da Concessão", - "CREATIONDATE": "Data de Criação", - "CHANGEDATE": "Última modificação", - "DATES": "Datas", - "ROLENAMESLIST": "Funções", - "NOROLES": "Nenhuma função", - "TYPE": "Tipo", - "TOAST": { - "PROJECTGRANTUSERGRANTADDED": "Concessão de projeto criada.", - "PROJECTGRANTADDED": "Concessão de projeto criada.", - "PROJECTGRANTCHANGED": "Concessão de projeto alterada.", - "PROJECTGRANTMEMBERADDED": "Gerente da concessão adicionado.", - "PROJECTGRANTMEMBERCHANGED": "Gerente da concessão alterado.", - "PROJECTGRANTMEMBERREMOVED": "Gerente da concessão removido.", - "PROJECTGRANTUPDATED": "Concessão de Projeto atualizada" - }, - "DIALOG": { - "DELETE_TITLE": "Excluir concessão de projeto", - "DELETE_DESCRIPTION": "Você está prestes a excluir uma concessão de projeto. Tem certeza?" - }, - "ROLES": "Funções do Projeto" - }, - "APP": { - "TITLE": "Aplicativos", - "NAME": "Nome", - "NAMEREQUIRED": "É necessário um nome." - }, - "ROLE": { - "EMPTY": "Nenhuma função foi criada ainda.", - "ADDNEWLINE": "Adicionar função adicional", - "KEY": "Chave", - "TITLE": "Funções", - "DESCRIPTION": "Defina algumas funções que podem ser usadas para criar concessões de projeto.", - "NAME": "Nome", - "DISPLAY_NAME": "Nome de Exibição", - "GROUP": "Grupo", - "ACTIONS": "Ações", - "ADDTITLE": "Criar Função", - "ADDDESCRIPTION": "Insira os dados para a nova função.", - "EDITTITLE": "Editar Função", - "EDITDESCRIPTION": "Insira os novos dados para a função.", - "DELETE": "Excluir Função", - "CREATIONDATE": "Criada em", - "CHANGEDATE": "Última modificação", - "SELECTGROUPTOOLTIP": "Selecione todas as funções do grupo {{group}}.", - "OPTIONS": "Opções", - "ASSERTION": "Afirma Funções na Autenticação", - "ASSERTION_DESCRIPTION": "As informações de função são enviadas pelo ponto de extremidade Userinfo e dependendo das configurações do seu aplicativo em tokens e outros tipos.", - "CHECK": "Verificar autorização na Autenticação", - "CHECK_DESCRIPTION": "Se definido, os usuários só podem fazer login se alguma função for atribuída à sua conta.", - "DIALOG": { - "DELETE_TITLE": "Excluir função", - "DELETE_DESCRIPTION": "Você está prestes a excluir uma função de projeto. Tem certeza?" - } - }, - "HAS_PROJECT": "Verificar Projeto na Autenticação", - "HAS_PROJECT_DESCRIPTION": "Verifica se a organização dousuário possui este projeto. Se não possuir, o usuário não poderá ser autenticado.", - "TABLE": { - "TOTAL": "Total de Entradas:", - "SELECTION": "Elementos Selecionados", - "DEACTIVATE": "Desativar Projeto", - "ACTIVATE": "Ativar Projeto", - "DELETE": "Excluir Projeto", - "ORGNAME": "Nome da Organização", - "ORGDOMAIN": "Domínio da Organização", - "STATE": "Status", - "TYPE": "Tipo", - "CREATIONDATE": "Criado em", - "CHANGEDATE": "Última modificação", - "RESOURCEOWNER": "Proprietário do Recurso", - "SHOWTABLE": "Mostrar tabela", - "SHOWGRID": "Mostrar grade", - "EMPTY": "Nenhum projeto encontrado" - }, - "TOAST": { - "MEMBERREMOVED": "Gerente removido.", - "MEMBERSADDED": "Gerentes adicionados.", - "MEMBERADDED": "Gerente adicionado.", - "MEMBERCHANGED": "Gerente alterado.", - "ROLESCREATED": "Funções criadas.", - "ROLEREMOVED": "Função removida.", - "ROLECHANGED": "Função alterada.", - "REACTIVATED": "Reativado.", - "DEACTIVATED": "Desativado.", - "CREATED": "Projeto criado.", - "UPDATED": "Projeto alterado.", - "GRANTUPDATED": "Concessão alterada.", - "DELETED": "Projeto excluído." - } - }, - "ROLES": { - "DIALOG": { - "DELETE_TITLE": "Excluir função", - "DELETE_DESCRIPTION": "Você está prestes a excluir uma função. Tem certeza?" - } - }, - "NEXTSTEPS": { - "TITLE": "Próximos Passos" - }, - "IDP": { - "LIST": { - "TITLE": "Provedores de Identidade", - "DESCRIPTION": "Gerencie a configuração do seu Provedor de Identidade, que pode ser ativado nas suas Configurações de Login.", - "ACTIVETITLE": "Provedores de Identidade Ativos" - }, - "CREATE": { - "TITLE": "Adicionar provedor", - "DESCRIPTION": "Selecione um ou mais dos seguintes provedores.", - "STEPPERTITLE": "Criar Provedor", - "OIDC": { - "TITLE": "Provedor OIDC", - "DESCRIPTION": "Insira os dados necessários para o seu provedor OIDC." - }, - "OAUTH": { - "TITLE": "Provedor OAuth", - "DESCRIPTION": "Insira os dados necessários para o seu provedor OAuth." - }, - "JWT": { - "TITLE": "Provedor JWT", - "DESCRIPTION": "Insira os dados necessários para o seu provedor JWT." - }, - "GOOGLE": { - "TITLE": "Provedor Google", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Google" - }, - "GITLAB": { - "TITLE": "Provedor Gitlab", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Gitlab" - }, - "GITLABSELFHOSTED": { - "TITLE": "Provedor Gitlab Self Hosted", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Gitlab Self Hosted" - }, - "GITHUBES": { - "TITLE": "Provedor GitHub Enterprise Server", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do GitHub Enterprise Server" - }, - "GITHUB": { - "TITLE": "Provedor Github", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Github" - }, - "AZUREAD": { - "TITLE": "Provedor Microsoft", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade da Microsoft" - }, - "LDAP": { - "TITLE": "Active Directory / LDAP", - "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade LDAP" - } - }, - "DETAIL": { - "TITLE": "Provedor de Identidade", - "DESCRIPTION": "Atualize a configuração do seu provedor", - "DATECREATED": "Criado em", - "DATECHANGED": "Última modificação" - }, - "OPTIONS": { - "ISAUTOCREATION": "Criação Automática", - "ISAUTOCREATION_DESC": "Se selecionado, uma conta será criada se ainda não existir.", - "ISAUTOUPDATE": "Atualização Automática", - "ISAUTOUPDATE_DESC": "Se selecionado, as contas são atualizadas ao serem reautenticadas.", - "ISCREATIONALLOWED": "Criação de Conta Permitida", - "ISCREATIONALLOWED_DESC": "Determina se as contas podem ser criadas.", - "ISLINKINGALLOWED": "Vinculação de Conta Permitida", - "ISLINKINGALLOWED_DESC": "Determina se uma identidade pode ser vinculada a uma conta existente." - }, - "OWNERTYPES": { - "0": "desconhecido", - "1": "Instância", - "2": "Organização" - }, - "STATES": { - "1": "ativo", - "2": "inativo" - }, - "AZUREADTENANTTYPES": { - "3": "ID do Locatário", - "0": "Comum", - "1": "Organizações", - "2": "Consumidores" - }, - "AZUREADTENANTTYPE": "Tipo de Locatário", - "AZUREADTENANTID": "ID do Locatário", - "EMAILVERIFIED": "E-mail Verificado", - "NAMEHINT": "Se especificado, será mostrado na interface de login.", - "OPTIONAL": "opcional", - "LDAPATTRIBUTES": "Atributos LDAP", - "UPDATEBINDPASSWORD": "atualizar Senha de Vinculação", - "UPDATECLIENTSECRET": "atualizar segredo do cliente", - "ADD": "Adicionar Provedor de Identidade", - "TYPE": "Tipo", - "OWNER": "Proprietário", - "ID": "ID", - "NAME": "Nome", - "AUTHORIZATIONENDPOINT": "Ponto de Autorização", - "TOKENENDPOINT": "Ponto de Token", - "USERENDPOINT": "Ponto de Usuário", - "IDATTRIBUTE": "Atributo ID", - "AVAILABILITY": "Disponibilidade", - "AVAILABLE": "disponível", - "AVAILABLEBUTINACTIVE": "disponível, mas inativo", - "SETAVAILABLE": "definir como disponível", - "SETUNAVAILABLE": "definir como indisponível", - "CONFIG": "Configuração", - "STATE": "Status", - "ISSUER": "Eu emissor", - "SCOPESLIST": "Lista de Escopos", - "CLIENTID": "ID do Cliente", - "CLIENTSECRET": "Segredo do Cliente", - "LDAPCONNECTION": "Conexão", - "LDAPUSERBINDING": "Vínculo de Usuário", - "BASEDN": "BaseDn", - "BINDDN": "BindDn", - "BINDPASSWORD": "Senha de Vinculação", - "SERVERS": "Servidores", - "STARTTLS": "Iniciar TLS", - "TIMEOUT": "Tempo limite em segundos", - "USERBASE": "Base de Usuários", - "USERFILTERS": "Filtros de Usuário", - "USEROBJECTCLASSES": "Classes de Objeto de Usuário", - "REQUIRED": "obrigatório", - "LDAPIDATTRIBUTE": "atributo ID", - "AVATARURLATTRIBUTE": "atributo URL de Avatar", - "DISPLAYNAMEATTRIBUTE": "atributo de Nome Exibido", - "EMAILATTRIBUTEATTRIBUTE": "atributo de E-mail", - "EMAILVERIFIEDATTRIBUTE": "atributo de E-mail Verificado", - "FIRSTNAMEATTRIBUTE": "atributo de Nome Próprio", - "LASTNAMEATTRIBUTE": "atributo de Sobrenome", - "NICKNAMEATTRIBUTE": "atributo de Apelido", - "PHONEATTRIBUTE": "atributo de Telefone", - "PHONEVERIFIEDATTRIBUTE": "atributo de Telefone Verificado", - "PREFERREDLANGUAGEATTRIBUTE": "atributo de Idioma Preferido", - "PREFERREDUSERNAMEATTRIBUTE": "atributo de Nome de Usuário Preferido", - "PROFILEATTRIBUTE": "atributo de Perfil", - "IDPDISPLAYNAMMAPPING": "Mapeamento de Nome de Exibição do IDP", - "USERNAMEMAPPING": "Mapeamento de Nome de Usuário", - "DATES": "Datas", - "CREATIONDATE": "Criado em", - "CHANGEDATE": "Última Modificação", - "DEACTIVATE": "Desativar", - "ACTIVATE": "Ativar", - "DELETE": "Excluir", - "DELETE_TITLE": "Excluir Provedor de Identidade", - "DELETE_DESCRIPTION": "Você está prestes a excluir um provedor de identidade. As alterações resultantes são irreversíveis. Você realmente deseja fazer isso?", - "DELETE_SELECTION_TITLE": "Excluir Provedor de Identidade", - "DELETE_SELECTION_DESCRIPTION": "Você está prestes a excluir um provedor de identidade. As alterações resultantes são irreversíveis. Você realmente deseja fazer isso?", - "EMPTY": "Nenhum provedor de identidade disponível", - "OIDC": { - "GENERAL": "Informações Gerais", - "TITLE": "Configuração OIDC", - "DESCRIPTION": "Insira os dados para o Provedor de Identidade OIDC." - }, - "JWT": { - "TITLE": "Configuração JWT", - "DESCRIPTION": "Insira os dados para o Provedor de Identidade JWT.", - "HEADERNAME": "Nome do Cabeçalho", - "JWTENDPOINT": "Endpoint JWT", - "JWTKEYSENDPOINT": "Endpoint de Chaves JWT" - }, - "TOAST": { - "SAVED": "Salvo com sucesso.", - "REACTIVATED": "Provedor de identidade reativado.", - "DEACTIVATED": "Provedor de identidade desativado.", - "SELECTEDREACTIVATED": "Provedores de identidade selecionados reativados.", - "SELECTEDDEACTIVATED": "Provedores de identidade selecionados desativados.", - "SELECTEDKEYSDELETED": "Provedores de identidade selecionados excluídos.", - "DELETED": "Provedor de identidade removido com sucesso!", - "ADDED": "Adicionado com sucesso.", - "REMOVED": "Removido com sucesso." - } - }, - "MFA": { - "LIST": { - "MULTIFACTORTITLE": "Sem Senha", - "MULTIFACTORDESCRIPTION": "Defina seus Multifactores para autenticação sem senha aqui.", - "SECONDFACTORTITLE": "Autenticação Multifator", - "SECONDFACTORDESCRIPTION": "Defina outros fatores possíveis com os quais você pode proteger a autenticação por senha." - }, - "CREATE": { - "TITLE": "Novo Fator", - "DESCRIPTION": "Selecione o tipo do novo fator." - }, - "DELETE": { - "TITLE": "Excluir Fator", - "DESCRIPTION": "Você está prestes a excluir um fator das Configurações de Login. Tem certeza?" - }, - "TOAST": { - "ADDED": "Adicionado com sucesso.", - "SAVED": "Salvo com sucesso.", - "DELETED": "Removido com sucesso" - }, - "TYPE": "Tipo", - "MULTIFACTORTYPES": { - "0": "Desconhecido", - "1": "Impressão Digital, Chaves de Segurança, Face ID e outros" - }, - "SECONDFACTORTYPES": { - "0": "Desconhecido", - "1": "Senha de Uso Único (OTP)", - "2": "Impressão Digital, Chaves de Segurança, Face ID e outros" - } - }, - "LOGINPOLICY": { - "CREATE": { - "TITLE": "Configurações de Login", - "DESCRIPTION": "Defina como seus usuários podem ser autenticados na sua organização." - }, - "IDPS": "Provedores de Identidade", - "ADDIDP": { - "TITLE": "Adicionar Provedor de Identidade", - "DESCRIPTION": "Você pode selecionar provedores pré-definidos ou criados por você para autenticação.", - "SELECTIDPS": "Provedores de identidade" - }, - "PASSWORDLESS": "Login sem Senha", - "PASSWORDLESSTYPE": { - "0": "Não permitido", - "1": "Permitido" - } - }, - "APP": { - "LIST": "Aplicações", - "COMPLIANCE": "Conformidade OIDC", - "URLS": "URLs", - "CONFIGURATION": "Configuração", - "TOKEN": "Configurações de Token", - "PAGES": { - "TITLE": "Aplicativo", - "ID": "ID", - "DESCRIPTION": "Aqui você pode editar os dados do seu aplicativo e sua configuração.", - "CREATE": "Criar aplicativo", - "CREATE_SELECT_PROJECT": "Selecione o seu projeto primeiro", - "CREATE_NEW_PROJECT": "ou crie um novo aqui.", - "CREATE_DESC_TITLE": "Insira os Detalhes do seu Aplicativo Passo a Passo", - "CREATE_DESC_SUB": "Uma configuração recomendada será gerada automaticamente.", - "STATE": "Status", - "DATECREATED": "Criado", - "DATECHANGED": "Alterado", - "URLS": "URLs", - "DELETE": "Excluir App", - "DETAIL": { - "TITLE": "Detalhe", - "STATE": { - "0": "Não definido", - "1": "Ativo", - "2": "Inativo" - } - }, - "DIALOG": { - "CONFIG": { - "TITLE": "Alterar Configuração OIDC" - }, - "DELETE": { - "TITLE": "Excluir App", - "DESCRIPTION": "Você realmente deseja excluir este aplicativo?" - } - }, - "NEXTSTEPS": { - "TITLE": "Próximos Passos", - "0": { - "TITLE": "Adicionar funções", - "DESC": "Insira as funções do seu projeto" - }, - "1": { - "TITLE": "Adicionar usuários", - "DESC": "Adicione novos usuários da sua organização" - }, - "2": { - "TITLE": "Ajuda e Suporte", - "DESC": "Leia nossa documentação sobre como criar aplicativos ou entre em contato com nosso suporte" - } - } - }, - "NAMEDIALOG": { - "TITLE": "Renomear Aplicativo", - "DESCRIPTION": "Digite o novo nome para o seu aplicativo", - "NAME": "Novo Nome" - }, - "NAME": "Nome", - "TYPE": "Tipo de Aplicativo", - "AUTHMETHOD": "Método de Autenticação", - "AUTHMETHODSECTION": "Método de Autenticação", - "GRANT": "Tipos de Concessão", - "ADDITIONALORIGINS": "Origens Adicionais", - "ADDITIONALORIGINSDESC": "Se você deseja adicionar origens adicionais ao seu aplicativo que não são usadas como redirecionamento, você pode fazer isso aqui.", - "ORIGINS": "Origens", - "NOTANORIGIN": "O valor inserido não é uma origem", - "PROSWITCH": "Sou um profissional. Pular este assistente.", - "NAMEANDTYPESECTION": "Nome e Tipo", - "TITLEFIRST": "Nome do aplicativo", - "TYPETITLE": "Tipo de aplicativo", - "OIDC": { - "WELLKNOWN": "Links adicionais podem ser obtidos a partir do endpoint de descoberta.", - "INFO": { - "ISSUER": "Emissor", - "CLIENTID": "ID do Cliente" - }, - "CURRENT": "Configuração Atual", - "TOKENSECTIONTITLE": "Opções de Token de Autenticação", - "REDIRECTSECTIONTITLE": "Configurações de Redirecionamento", - "REDIRECTTITLE": "Especifique as URIs para onde o login será redirecionado.", - "POSTREDIRECTTITLE": "Esta é a URI de redirecionamento após o logout.", - "REDIRECTDESCRIPTIONWEB": "URIs de redirecionamento devem começar com https://. http:// é válido apenas com o modo de desenvolvimento habilitado.", - "REDIRECTDESCRIPTIONNATIVE": "URIs de redirecionamento devem começar com seu próprio protocolo, http://127.0.0.1, http://[::1] ou http://localhost.", - "REDIRECTNOTVALID": "Esta URI de redirecionamento não é válida.", - "COMMAORENTERSEPERATION": "separar com ↵", - "TYPEREQUIRED": "O tipo é obrigatório.", - "TITLE": "Configuração OIDC", - "CLIENTID": "ID do Cliente", - "CLIENTSECRET": "Segredo do Cliente", - "CLIENTSECRET_NOSECRET": "Com o fluxo de autenticação escolhido, nenhum segredo é necessário e, portanto, não está disponível.", - "CLIENTSECRET_DESCRIPTION": "Mantenha o segredo do cliente em um local seguro, pois ele desaparecerá assim que o diálogo for fechado.", - "REGENERATESECRET": "Regenerar Segredo do Cliente", - "DEVMODE": "Modo de Desenvolvimento", - "DEVMODEDESC": "Atenção: Com o modo de desenvolvimento habilitado, as URIs de redirecionamento não serão validadas.", - "SKIPNATIVEAPPSUCCESSPAGE": "Pular Página de Sucesso de Login", - "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Pule a página de sucesso após o login para este aplicativo nativo.", - "REDIRECT": "URIs de Redirecionamento", - "REDIRECTSECTION": "URIs de Redirecionamento", - "POSTLOGOUTREDIRECT": "URIs de Pós-Logout", - "RESPONSESECTION": "Tipos de Resposta", - "GRANTSECTION": "Tipos de Concessão", - "GRANTTITLE": "Selecione os tipos de concessão. Observação: O tipo Implícito está disponível apenas para aplicativos baseados em navegador.", - "APPTYPE": { - "0": "Web", - "1": "Agente de Usuário", - "2": "Nativo" - }, - "RESPONSETYPE": "Tipos de Resposta", - "RESPONSE": { - "0": "Código", - "1": "ID Token", - "2": "Token-ID Token" - }, - "REFRESHTOKEN": "Token de Atualização", - "GRANTTYPE": "Tipos de Concessão", - "GRANT": { - "0": "Código de Autorização", - "1": "Implícito", - "2": "Token de Atualização", - "3": "Código do Dispositivo" - }, - "AUTHMETHOD": { - "0": "Básico", - "1": "Post", - "2": "Nenhum", - "3": "JWT de Chave Privada" - }, - "TOKENTYPE": "Tipo de Token de Autenticação", - "TOKENTYPE0": "Token de Portador", - "TOKENTYPE1": "JWT", - "UNSECUREREDIRECT": "Espero que você saiba o que está fazendo.", - "OVERVIEWSECTION": "Visão Geral", - "OVERVIEWTITLE": "Agora você terminou. Revise sua configuração.", - "ACCESSTOKENROLEASSERTION": "Adicionar funções do usuário ao token de acesso", - "ACCESSTOKENROLEASSERTION_DESCRIPTION": "Se selecionado, as funções solicitadas do usuário autenticado serão adicionadas ao token de acesso.", - "IDTOKENROLEASSERTION": "Funções do usuário no ID Token", - "IDTOKENROLEASSERTION_DESCRIPTION": "Se selecionado, as funções solicitadas do usuário autenticado serão adicionadas ao ID token.", - "IDTOKENUSERINFOASSERTION": "Informações do usuário no ID Token", - "IDTOKENUSERINFOASSERTION_DESCRIPTION": "Permite que os clientes obtenham claims de perfil, e-mail, telefone e endereço do ID token.", - "CLOCKSKEW": "Permite que os clientes lidem com a discrepância de tempo do OP e do cliente. A duração (0-5s) será adicionada ao claim exp e subtraída de iat, auth_time e nbf.", - "RECOMMENDED": "recomendado", - "NOTRECOMMENDED": "não recomendado", - "SELECTION": { - "APPTYPE": { - "WEB": { - "TITLE": "Web", - "DESCRIPTION": "Aplicações da Web regulares como .net, PHP, Node.js, Java, etc." - }, - "NATIVE": { - "TITLE": "Nativo", - "DESCRIPTION": "Aplicativos móveis, desktop, dispositivos inteligentes, etc." - }, - "USERAGENT": { - "TITLE": "Agente de Usuário", - "DESCRIPTION": "Aplicativos de página única (SPA) e, em geral, todos os frameworks JS executados em navegadores" - } - } - } - }, - "API": { - "INFO": { - "CLIENTID": "ID do Cliente" - }, - "REGENERATESECRET": "Regenerar Segredo do Cliente", - "SELECTION": { - "TITLE": "API", - "DESCRIPTION": "APIs em geral" - }, - "AUTHMETHOD": { - "0": "Básico", - "1": "JWT de Chave Privada" - } - }, - "SAML": { - "SELECTION": { - "TITLE": "SAML", - "DESCRIPTION": "Aplicativos SAML" - }, - "CONFIGSECTION": "Configuração SAML", - "URL": "URL onde o arquivo de Metadados está localizado", - "OR": "ou", - "XML": "Carregar XML de Metadados", - "METADATA": "Metadados", - "METADATAFROMFILE": "Metadados do Arquivo" - }, - "AUTHMETHODS": { - "CODE": { - "TITLE": "Código", - "DESCRIPTION": "Trocar o código de autorização pelos tokens" - }, - "PKCE": { - "TITLE": "PKCE", - "DESCRIPTION": "Usar um hash aleatório em vez de um segredo de cliente estático para maior segurança" - }, - "POST": { - "TITLE": "POST", - "DESCRIPTION": "Enviar client_id e client_secret como parte do formulário" - }, - "PK_JWT": { - "TITLE": "JWT de Chave Privada", - "DESCRIPTION": "Usar uma chave privada para autorizar seu aplicativo" - }, - "BASIC": { - "TITLE": "Básico", - "DESCRIPTION": "Autenticação com Nome de Usuário e Senha" - }, - "IMPLICIT": { - "TITLE": "Implícito", - "DESCRIPTION": "Obter os tokens diretamente do ponto de extremidade de autorização" - }, - "DEVICECODE": { - "TITLE": "Código do Dispositivo", - "DESCRIPTION": "Autorizar o dispositivo em um computador ou smartphone." - }, - "CUSTOM": { - "TITLE": "Personalizado", - "DESCRIPTION": "Sua configuração não corresponde a nenhuma outra opção." - } - }, - "TOAST": { - "REACTIVATED": "Aplicativo reativado.", - "DEACTIVATED": "Aplicativo desativado.", - "OIDCUPDATED": "Aplicativo atualizado.", - "APIUPDATED": "Aplicativo atualizado.", - "UPDATED": "Aplicativo atualizado.", - "CREATED": "Aplicativo criado.", - "CLIENTSECRETREGENERATED": "segredo do cliente gerado.", - "DELETED": "Aplicativo excluído.", - "CONFIGCHANGED": "Alterações detectadas!" - } - }, - "GENDERS": { - "0": "Desconhecido", - "1": "Feminino", - "2": "Masculino", - "3": "Outro" - }, - "LANGUAGES": { - "de": "Deutsch", - "en": "English", - "es": "Español", - "fr": "Français", - "it": "Italiano", - "ja": "日本語", - "pl": "Polski", - "zh": "简体中文", - "bg": "Български", - "pt": "Portuguese", - "mk": "Македонски" - }, - "MEMBER": { - "ADD": "Adicionar um Gerente", - "CREATIONTYPE": "Tipo de Criação", - "CREATIONTYPES": { - "3": "IAM", - "2": "Organização", - "0": "Projeto Próprio", - "1": "Projeto Concedido", - "4": "Projeto" - }, - "EDITROLE": "Editar funções", - "EDITFOR": "Editar as funções para o usuário: {{value}}", - "DIALOG": { - "DELETE_TITLE": "Remover Gerente", - "DELETE_DESCRIPTION": "Você está prestes a remover um gerente. Tem certeza?" - } - }, - "ROLESLABEL": "Funções", - "GRANTS": { - "TITLE": "Autorizações", - "DESC": "Estas são todas as autorizações na sua organização.", - "DELETE": "Excluir Autorização", - "EMPTY": "Nenhuma autorização encontrada", - "ADD": "Criar Autorização", - "ADD_BTN": "Novo", - "PROJECT": { - "TITLE": "Autorização", - "DESCRIPTION": "Defina as autorizações para o projeto especificado. Observe que você só pode ver entradas de projetos e usuários para os quais você tem permissão." - }, - "USER": { - "TITLE": "Autorização", - "DESCRIPTION": "Defina as autorizações para o usuário especificado. Observe que você só pode ver entradas de projetos e usuários para os quais você tem permissão." - }, - "CREATE": { - "TITLE": "Criar autorização", - "DESCRIPTION": "Pesquise a organização, o projeto e as funções correspondentes." - }, - "EDIT": { - "TITLE": "Alterar autorização" - }, - "DETAIL": { - "TITLE": "Detalhes da Autorização", - "DESCRIPTION": "Aqui você pode ver todos os detalhes da autorização." - }, - "TOAST": { - "UPDATED": "Autorização atualizada.", - "REMOVED": "Autorização removida", - "BULKREMOVED": "Autorizações removidas." - }, - "DIALOG": { - "DELETE_TITLE": "Excluir autorização", - "DELETE_DESCRIPTION": "Você está prestesa excluir uma autorização. Deseja continuar?", - "BULK_DELETE_TITLE": "Excluir autorizações", - "BULK_DELETE_DESCRIPTION": "Você está prestes a excluir várias autorizações. Deseja continuar?" - } - }, - "CHANGES": { - "LISTTITLE": "Últimas Alterações", - "BOTTOM": "Você chegou ao final da lista.", - "LOADMORE": "Carregar mais", - "ORG": { - "TITLE": "Atividade", - "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração na organização." - }, - "PROJECT": { - "TITLE": "Atividade", - "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração no projeto." - }, - "USER": { - "TITLE": "Atividade", - "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração no usuário." - } - } -} \ No newline at end of file + "APP_NAME": "ZITADEL", + "PAGINATOR": { + "PREVIOUS": "Anterior", + "NEXT": "Próximo", + "COUNT": "Total de Resultados", + "MORE": "Mais" + }, + "FOOTER": { + "LINKS": { + "CONTACT": "Contato", + "TOS": "Termos de Serviço", + "PP": "Política de Privacidade" + }, + "THEME": { + "DARK": "Escuro", + "LIGHT": "Claro" + } + }, + "HOME": { + "WELCOME": "Comece com ZITADEL", + "DISCLAIMER": "A ZITADEL trata seus dados confidencialmente e com segurança.", + "DISCLAIMERLINK": "Mais informações", + "DOCUMENTATION": { + "TITLE": "Documentação", + "DESCRIPTION": "Comece rapidamente com ZITADEL." + }, + "GETSTARTED": { + "TITLE": "Comece com ZITADEL", + "DESCRIPTION": "Comece rapidamente com ZITADEL." + }, + "QUICKSTARTS": { + "LABEL": "Primeiros Passos", + "TITLE": "Início Rápido", + "DESCRIPTION": "Comece rapidamente com ZITADEL." + }, + "SHORTCUTS": { + "SHORTCUTS": "Atalhos", + "SETTINGS": "Atalhos disponíveis", + "PROJECTS": "Projetos", + "REORDER": "Mantenha e arraste o bloco para movê-lo", + "ADD": "Mantenha e arraste um bloco para adicionar" + } + }, + "ONBOARDING": { + "DESCRIPTION": "Seu processo de integração", + "MOREDESCRIPTION": "mais atalhos", + "COMPLETED": "concluído", + "DISMISS": "Não, obrigado, sou um profissional.", + "CARD": { + "TITLE": "Inicie o ZITADEL", + "DESCRIPTION": "Esta lista de verificação ajuda a configurar sua instância e orienta você nas etapas mais essenciais" + }, + "EVENTS": { + "instance.policy.label.added": { + "title": "Configure sua marca", + "description": "Defina cores e forma para o seu login e faça o upload do seu logotipo e ícones.", + "action": "Configurar marca" + }, + "instance.smtp.config.added": { + "title": "Configure as configurações SMTP", + "description": "Configure as configurações do seu próprio servidor de e-mail.", + "action": "Configurar SMTP" + }, + "project.added": { + "title": "Crie um projeto", + "description": "Adicione um projeto e defina suas funções e autorizações.", + "action": "Criar projeto" + }, + "project.application.added": { + "title": "Crie um aplicativo", + "description": "Crie um aplicativo da web, nativo, API ou SAML e configure o fluxo de autenticação.", + "action": "Criar aplicativo" + }, + "user.human.added": { + "title": "Adicione usuários", + "description": "Adicione usuários do seu aplicativo", + "action": "Adicionar usuário" + }, + "user.grant.added": { + "title": "Conceda permissões aos usuários", + "description": "Permita que os usuários acessem seu aplicativo e configure suas funções.", + "action": "Conceder permissões" + } + } + }, + "MENU": { + "INSTANCE": "Instância", + "DASHBOARD": "Início", + "PERSONAL_INFO": "Informações Pessoais", + "DOCUMENTATION": "Documentação", + "INSTANCEOVERVIEW": "Instância", + "ORGS": "Organizações", + "VIEWS": "Visualizações", + "EVENTS": "Eventos", + "FAILEDEVENTS": "Eventos com Falha", + "ORGANIZATION": "Organização", + "DOMAINS": "Domínios", + "PROJECT": "Projetos", + "PROJECTOVERVIEW": "Visão Geral", + "PROJECTGRANTS": "Autorizações", + "ROLES": "Funções", + "GRANTEDPROJECT": "Projetos Concedidos", + "HUMANUSERS": "Usuários", + "MACHINEUSERS": "Usuários de Serviço", + "LOGOUT": "Encerrar Sessão de Todos os Usuários", + "NEWORG": "Nova Organização", + "IAMADMIN": "Você é um Administrador IAM. Observe que você possui permissões estendidas.", + "SHOWORGS": "Mostrar Todas as Organizações", + "GRANTS": "Autorizações", + "ACTIONS": "Ações", + "PRIVACY": "Privacidade", + "TOS": "Termos de Serviço", + "OPENSHORTCUTSTOOLTIP": "Digite ? para mostrar os atalhos do teclado", + "SETTINGS": "Configurações", + "CUSTOMERPORTAL": "Portal do Cliente" + }, + "ACTIONS": { + "ACTIONS": "Ações", + "FILTER": "Filtrar", + "RENAME": "Renomear", + "SET": "Definir", + "COPY": "Copiar para a Área de Transferência", + "COPIED": "Copiado para a área de transferência.", + "RESET": "Redefinir", + "RESETDEFAULT": "Redefinir para o Padrão", + "RESETTO": "Redefinir para: ", + "RESETCURRENT": "Redefinir para o atual", + "SHOW": "Mostrar", + "HIDE": "Ocultar", + "SAVE": "Salvar", + "SAVENOW": "Salvar agora", + "NEW": "Novo", + "ADD": "Adicionar", + "CREATE": "Criar", + "CONTINUE": "Continuar", + "BACK": "Voltar", + "CLOSE": "Fechar", + "CLEAR": "Limpar", + "CANCEL": "Cancelar", + "INFO": "Informações", + "OK": "OK", + "SELECT": "Selecionar", + "VIEW": "Mostrar", + "SELECTIONDELETE": "Excluir seleção", + "DELETE": "Excluir", + "REMOVE": "Remover", + "VERIFY": "Verificar", + "FINISH": "Concluir", + "FINISHED": "Fechar", + "CHANGE": "Alterar", + "REACTIVATE": "Reativar", + "ACTIVATE": "Ativar", + "DEACTIVATE": "Desativar", + "REFRESH": "Atualizar", + "LOGIN": "Entrar", + "EDIT": "Editar", + "PIN": "Fixar / Desafixar", + "CONFIGURE": "Configurar", + "SEND": "Enviar", + "NEWVALUE": "Novo Valor", + "RESTORE": "Restaurar", + "CONTINUEWITHOUTSAVE": "Continuar sem salvar", + "OF": "de", + "PREVIOUS": "Anterior", + "NEXT": "Próximo", + "MORE": "mais", + "STEP": "Passo", + "SETUP": "Configuração", + "UNSAVEDCHANGES": "Alterações não salvas", + "UNSAVED": { + "DIALOG": { + "DESCRIPTION": "Tem certeza de que deseja descartar esta nova ação? Sua ação será perdida", + "CANCEL": "Cancelar", + "DISCARD": "Descartar" + } + }, + "TABLE": { + "SHOWUSER": "Mostrar usuário {{value}}" + } + }, + "MEMBERROLES": { + "IAM_OWNER": "Tem controle sobre toda a instância, incluindo todas as organizações", + "IAM_OWNER_VIEWER": "Tem permissão para revisar toda a instância, incluindo todas as organizações", + "IAM_ORG_MANAGER": "Tem permissão para criar e gerenciar organizações", + "IAM_USER_MANAGER": "Tem permissão para criar e gerenciar usuários", + "ORG_OWNER": "Tem permissão sobre toda a organização", + "ORG_USER_MANAGER": "Tem permissão para criar e gerenciar usuários da organização", + "ORG_OWNER_VIEWER": "Tem permissão para revisar toda a organização", + "ORG_USER_PERMISSION_EDITOR": "Tem permissão para gerenciar concessões de usuários", + "ORG_PROJECT_PERMISSION_EDITOR": "Tem permissão para gerenciar concessões de projetos", + "ORG_PROJECT_CREATOR": "Tem permissão para criar seus próprios projetos e configurações subjacentes", + "PROJECT_OWNER": "Tem permissão sobre todo o projeto", + "PROJECT_OWNER_VIEWER": "Tem permissão para revisar todo o projeto", + "PROJECT_OWNER_GLOBAL": "Tem permissão sobre todo o projeto", + "PROJECT_OWNER_VIEWER_GLOBAL": "Tem permissão para revisar todo o projeto", + "PROJECT_GRANT_OWNER": "Tem permissão para gerenciar a concessão do projeto", + "PROJECT_GRANT_OWNER_VIEWER": "Tem permissão para revisar a concessão do projeto" + }, + "OVERLAYS": { + "ORGSWITCHER": { + "TEXT": "Todas as configurações e tabelas de organização no console são baseadas em uma organização selecionada. Clique neste botão para trocar de organização ou criar uma nova." + }, + "INSTANCE": { + "TEXT": "Clique aqui para acessar as configurações da instância. Observe que você só tem acesso a este botão se tiver permissões avançadas." + }, + "PROFILE": { + "TEXT": "Aqui você pode alternar entre suas contas de usuário e gerenciar suas sessões e perfil." + }, + "NAV": { + "TEXT": "Esta navegação muda com base na organização selecionada acima ou na sua instância" + }, + "CONTEXTCHANGED": { + "TEXT": "O contexto da organização foi alterado." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "A visualização mudou para a instância!" + } + }, + "FILTER": { + "TITLE": "Filtro", + "STATE": "Status", + "DISPLAYNAME": "Nome de Exibição do Usuário", + "EMAIL": "E-mail", + "USERNAME": "Nome de Usuário", + "ORGNAME": "Nome da Organização", + "PROJECTNAME": "Nome do Projeto", + "RESOURCEOWNER": "Proprietário do Recurso", + "METHODS": { + "5": "contém", + "7": "termina com", + "1": "igual a" + } + }, + "KEYBOARDSHORTCUTS": { + "TITLE": "Atalhos de Teclado", + "UNDERORGCONTEXT": "Dentro das páginas da organização", + "SIDEWIDE": "Atalhos em todo o site", + "SHORTCUTS": { + "HOME": "Ir para Início", + "INSTANCE": "Ir para Instância", + "ORG": "Ir para Organização", + "ORGSETTINGS": "Ir para Configurações da Organização", + "ORGSWITCHER": "Trocar de Organização", + "ME": "Ir para meu perfil", + "PROJECTS": "Ir para Projetos", + "USERS": "Ir para Usuários", + "USERGRANTS": "Ir para Autorizações", + "ACTIONS": "Ir para Ações e Fluxos", + "DOMAINS": "Ir para Domínios" + } + }, + "RESOURCEID": "ID do Recurso", + "NAME": "Nome", + "VERSION": "Versão", + "TABLE": { + "NOROWS": "Sem dados" + }, + "ERRORS": { + "REQUIRED": "Por favor, preencha este campo.", + "ATLEASTONE": "Forneça pelo menos um valor.", + "TOKENINVALID": { + "TITLE": "Seu token de autorização expirou.", + "DESCRIPTION": "Clique no botão abaixo para fazer login novamente." + }, + "EXHAUSTED": { + "TITLE": "Sua cota para solicitações autenticadas está esgotada.", + "DESCRIPTION": "Remova ou aumente o limite de cota para esta instância ZITADEL." + }, + "INVALID_FORMAT": "O formato é inválido.", + "NOTANEMAIL": "O valor fornecido não é um endereço de e-mail.", + "MINLENGTH": "Deve ter pelo menos {{requiredLength}} caracteres.", + "UPPERCASEMISSING": "Deve incluir uma letra maiúscula.", + "LOWERCASEMISSING": "Deve incluir uma letra minúscula.", + "SYMBOLERROR": "Deve incluir um símbolo ou caractere de pontuação.", + "NUMBERERROR": "Deve incluir um dígito.", + "PWNOTEQUAL": "As senhas fornecidas não correspondem.", + "PHONE": "O número de telefone deve começar com 00 ou +." + }, + "USER": { + "SETTINGS": { + "TITLE": "Configurações", + "GENERAL": "Geral", + "IDP": "Provedores de Identidade", + "SECURITY": "Senha e Segurança", + "KEYS": "Chaves", + "PAT": "Tokens de Acesso Pessoal", + "USERGRANTS": "Autorizações", + "MEMBERSHIPS": "Associações", + "METADATA": "Metadados" + }, + "TITLE": "Informações Pessoais", + "DESCRIPTION": "Gerencie suas informações e configurações de segurança.", + "PAGES": { + "LIST": "Usuários", + "TITLE": "Usuário", + "DESCRIPTION": "Crie novos usuários em sua organização e gerencie os existentes.", + "LISTMACHINE": "Usuários de Serviço", + "DESCRIPTIONMACHINE": "Crie e gerencie Usuários de Serviço de sua organização", + "DETAIL": "Detalhes", + "CREATE": "Criar", + "MY": "Minhas Informações", + "LOGINNAMES": "Nomes de Login", + "LOGINMETHODS": "Métodos de Login", + "LOGINNAMESDESC": "Estes são os seus nomes de login:", + "NOUSER": "Nenhum usuário associado.", + "REACTIVATE": "Reativar", + "DEACTIVATE": "Desativar", + "FILTER": "Filtro", + "STATE": "Status", + "DELETE": "Excluir Usuário", + "UNLOCK": "Desbloquear Usuário", + "GENERATESECRET": "Gerar Segredo do Cliente", + "REMOVESECRET": "Remover Segredo do Cliente", + "LOCKEDDESCRIPTION": "Este usuário foi bloqueado devido a exceder o número máximo de tentativas de login e deve ser desbloqueado para ser usado novamente.", + "DELETEACCOUNT": "Excluir Conta", + "DELETEACCOUNT_DESC": "Se você realizar esta ação, será desconectado e não terá mais acesso à sua conta. Essa ação não pode ser desfeita, portanto, prossiga com cautela.", + "DELETEACCOUNT_BTN": "Excluir Conta", + "DELETEACCOUNT_SUCCESS": "Conta excluída com sucesso!" + }, + "DETAILS": { + "DATECREATED": "Criado", + "DATECHANGED": "Alterado" + }, + "DIALOG": { + "DELETE_TITLE": "Excluir Usuário", + "DELETE_SELF_TITLE": "Excluir Conta", + "DELETE_DESCRIPTION": "Você está prestes a excluir permanentemente um usuário. Tem certeza?", + "DELETE_SELF_DESCRIPTION": "Você está prestes a excluir permanentemente sua conta pessoal. Isso irá desconectá-lo e excluir seu usuário. Essa ação não pode ser desfeita!", + "DELETE_AUTH_DESCRIPTION": "Você está prestes a excluir permanentemente sua conta pessoal. Tem certeza?", + "TYPEUSERNAME": "Digite '{{value}}' para confirmar e excluir o usuário.", + "USERNAME": "Nome de usuário", + "DELETE_BTN": "Excluir permanentemente" + }, + "SENDEMAILDIALOG": { + "TITLE": "Enviar Notificação por E-mail", + "DESCRIPTION": "Clique no botão abaixo para enviar uma notificação para o endereço de e-mail atual ou altere o endereço de e-mail no campo.", + "NEWEMAIL": "Novo endereço de e-mail" + }, + "SECRETDIALOG": { + "CLIENTSECRET": "Segredo do Cliente", + "CLIENTSECRET_DESCRIPTION": "Mantenha seu segredo do cliente em um local seguro, pois ele desaparecerá assim que o diálogo for fechado." + }, + "TABLE": { + "DEACTIVATE": "Desativar", + "ACTIVATE": "Ativar", + "CHANGEDATE": "Última Modificação", + "CREATIONDATE": "Criado Em", + "TYPES": { + "HUMAN": "Usuários", + "MACHINE": "Usuários de Serviço" + }, + "FILTER": { + "0": "Filtrar por Nome de Exibição", + "1": "Filtrar por Nome de Usuário", + "2": "filtrar por Nome de Exibição", + "3": "filtrar por Nome de Usuário", + "4": "filtrar por E-mail", + "5": "filtrar por Nome de Exibição", + "10": "filtrar por nome de organização", + "12": "filtrar por nome de projeto" + }, + "EMPTY": "Nenhum registro" + }, + "PASSWORDLESS": { + "SEND": "Enviar link de registro", + "TABLETYPE": "Tipo", + "TABLESTATE": "Status", + "NAME": "Nome", + "EMPTY": "Nenhum dispositivo configurado", + "TITLE": "Autenticação sem Senha", + "DESCRIPTION": "Adicione Métodos de Autenticação baseados em WebAuthn para fazerlogin no ZITADEL sem senha.", + "MANAGE_DESCRIPTION": "Gerencie os métodos de segundo fator dos seus usuários.", + "U2F": "Adicionar método", + "U2F_DIALOG_TITLE": "Verificar autenticador", + "U2F_DIALOG_DESCRIPTION": "Digite um nome para seu login sem senha usado", + "U2F_SUCCESS": "Autenticação sem senha criada com sucesso!", + "U2F_ERROR": "Ocorreu um erro durante a configuração!", + "U2F_NAME": "Nome do Autenticador", + "TYPE": { + "0": "Nenhum MFA definido", + "1": "One Time Password (OTP)", + "2": "Impressão digital, Chaves de Segurança, Face ID e outros" + }, + "STATE": { + "0": "Nenhum Estado", + "1": "Não Pronto", + "2": "Pronto", + "3": "Excluído" + }, + "DIALOG": { + "DELETE_TITLE": "Remover Método de Autenticação sem Senha", + "DELETE_DESCRIPTION": "Você está prestes a excluir um método de autenticação sem senha. Tem certeza?", + "ADD_TITLE": "Autenticação sem senha", + "ADD_DESCRIPTION": "Selecione uma das opções disponíveis para criar um método de autenticação sem senha.", + "SEND_DESCRIPTION": "Envie a si mesmo um link de registro para o seu endereço de e-mail.", + "SEND": "Enviar link de registro", + "SENT": "O e-mail foi entregue com sucesso. Verifique sua caixa de correio para continuar com a configuração.", + "QRCODE_DESCRIPTION": "Gerar um código QR para escanear com outro dispositivo.", + "QRCODE": "Gerar código QR", + "QRCODE_SCAN": "Escanear este código QR para continuar com a configuração no seu dispositivo.", + "NEW_DESCRIPTION": "Use este dispositivo para configurar a autenticação sem senha.", + "NEW": "Adicionar novo" + } + }, + "MFA": { + "TABLETYPE": "Tipo", + "TABLESTATE": "Status", + "NAME": "Nome", + "EMPTY": "Nenhum fator adicional", + "TITLE": "Autenticação Multifator", + "DESCRIPTION": "Adicione um segundo fator para garantir a segurança ideal da sua conta.", + "MANAGE_DESCRIPTION": "Gerencie os métodos de segundo fator dos seus usuários.", + "ADD": "Adicionar Fator", + "OTP": "Aplicativo de Autenticação para OTP (One-Time Password)", + "OTP_DIALOG_TITLE": "Adicionar OTP", + "OTP_DIALOG_DESCRIPTION": "Digitalize o código QR com um aplicativo autenticador e insira o código abaixo para verificar e ativar o método OTP.", + "U2F": "Impressão digital, Chaves de Segurança, Face ID e outros", + "U2F_DIALOG_TITLE": "Verificar Fator", + "U2F_DIALOG_DESCRIPTION": "Digite um nome para o seu fator de multifator universal usado.", + "U2F_SUCCESS": "Fator adicionado com sucesso!", + "U2F_ERROR": "Ocorreu um erro durante a configuração!", + "U2F_NAME": "Nome do Autenticador", + "TYPE": { + "0": "Nenhum MFA definido", + "1": "One Time Password (OTP)", + "2": "Impressão digital, Chaves de Segurança, Face ID e outros" + }, + "STATE": { + "0": "Nenhum Estado", + "1": "Não Pronto", + "2": "Pronto", + "3": "Excluído" + }, + "DIALOG": { + "MFA_DELETE_TITLE": "Remover Segundo Fator", + "MFA_DELETE_DESCRIPTION": "Você está prestes a excluir um segundo fator. Tem certeza?", + "ADD_MFA_TITLE": "Adicionar Segundo Fator", + "ADD_MFA_DESCRIPTION": "Selecione uma das opções disponíveis para criar um método de autenticação de segundo fator." + } + }, + "EXTERNALIDP": { + "TITLE": "Provedores de Identidade Externos", + "DESC": "", + "IDPCONFIGID": "ID da Configuração do Provedor de Identidade", + "IDPNAME": "Nome do Provedor de Identidade", + "USERDISPLAYNAME": "Nome Externo", + "EXTERNALUSERID": "ID do Usuário Externo", + "EMPTY": "Nenhum IDP externo encontrado", + "DIALOG": { + "DELETE_TITLE": "Remover IDP", + "DELETE_DESCRIPTION": "Você está prestes a excluir um Provedor de Identidade de um usuário. Deseja continuar?" + } + }, + "CREATE": { + "TITLE": "Criar um Novo Usuário", + "DESCRIPTION": "Forneça as informações necessárias.", + "NAMEANDEMAILSECTION": "Nome e E-mail", + "GENDERLANGSECTION": "Gênero e Idioma", + "PHONESECTION": "Números de Telefone", + "PASSWORDSECTION": "Senha Inicial", + "ADDRESSANDPHONESECTION": "Número de telefone", + "INITMAILDESCRIPTION": "Se ambas as opções forem selecionadas, nenhum e-mail de inicialização será enviado. Se apenas uma das opções for selecionada, um e-mail para fornecer/verificar os dados será enviado." + }, + "CODEDIALOG": { + "TITLE": "Verificar Número de Telefone", + "DESCRIPTION": "Digite o código que você recebeu por mensagem de texto para verificar seu número de telefone.", + "CODE": "Código" + }, + "DATA": { + "STATE": "Status", + "STATE0": "Desconhecido", + "STATE1": "Ativo", + "STATE2": "Inativo", + "STATE3": "Excluído", + "STATE4": "Bloqueado", + "STATE5": "Suspenso", + "STATE6": "Inicial" + }, + "PROFILE": { + "TITLE": "Perfil", + "EMAIL": "E-mail", + "PHONE": "Número de Telefone", + "PHONE_HINT": "Use 00 ou o símbolo + seguido do código de chamada do país, ou selecione o país na lista suspensa e, em seguida, insira o número de telefone", + "USERNAME": "Nome de Usuário", + "CHANGEUSERNAME": "modificar", + "CHANGEUSERNAME_TITLE": "Alterar nome de usuário", + "CHANGEUSERNAME_DESC": "Digite o novo nome no campo abaixo.", + "FIRSTNAME": "Nome Próprio", + "LASTNAME": "Sobrenome", + "NICKNAME": "Apelido", + "DISPLAYNAME": "Nome de Exibição", + "PREFERRED_LANGUAGE": "Idioma", + "GENDER": "Gênero", + "SENHA": "Senha", + "AVATAR": { + "UPLOADTITLE": "Carregar sua Foto de Perfil", + "UPLOADBTN": "Escolher arquivo", + "UPLOAD": "Carregar", + "CURRENT": "Foto Atual", + "PREVIEW": "Visualização", + "DELETESUCCESS": "Excluído com sucesso!", + "CROPPERERROR": "Ocorreu um erro ao carregar seu arquivo. Tente um formato e tamanho diferentes, se necessário." + }, + "COUNTRY": "País" + }, + "MACHINE": { + "TITLE": "Detalhes do Usuário de Serviço", + "USERNAME": "Nome de Usuário", + "NAME": "Nome", + "DESCRIPTION": "Descrição", + "KEYSTITLE": "Chaves", + "KEYSDESC": "Defina suas chaves e adicione uma data de expiração opcional.", + "TOKENSTITLE": "Tokens de Acesso Pessoal", + "TOKENSDESC": "Os tokens de acesso pessoal funcionam como tokens de acesso OAuth comuns.", + "ID": "ID da Chave", + "TYPE": "Tipo", + "EXPIRATIONDATE": "Data de Expiração", + "CHOOSEDATEAFTER": "Insira uma data de expiração válida após", + "CHOOSEEXPIRY": "Selecione uma data de expiração", + "CREATIONDATE": "Data de Criação", + "KEYDETAILS": "Detalhes da Chave", + "ACCESSTOKENTYPE": "Tipo de Token de Acesso", + "ACCESSTOKENTYPES": { + "0": "Bearer", + "1": "JWT" + }, + "ADD": { + "TITLE": "Adicionar Chave", + "DESCRIPTION": "Selecione o tipo de chave e escolha uma data de expiração opcional." + }, + "ADDED": { + "TITLE": "A chave foi criada", + "DESCRIPTION": "Baixe a chave, pois ela não será mais visível após o fechamento deste diálogo!" + }, + "KEYTYPES": { + "1": "JSON" + }, + "DIALOG": { + "DELETE_KEY": { + "TITLE": "Excluir Chave", + "DESCRIPTION": "Deseja excluir a chave selecionada? Essa ação não pode ser desfeita." + } + } + }, + "PASSWORD": { + "TITLE": "Senha", + "LABEL": "Uma senha segura ajuda a proteger a conta", + "DESCRIPTION": "Digite a nova senha de acordo com a política abaixo.", + "OLD": "Senha Atual", + "NEW": "Nova Senha", + "CONFIRM": "Confirmar Nova Senha", + "NEWINITIAL": "Senha", + "CONFIRMINITIAL": "Confirmar Senha", + "RESET": "Redefinir Senha Atual", + "SET": "Definir Nova Senha", + "RESENDNOTIFICATION": "Enviar Link de Redefinição de Senha", + "REQUIRED": "Algumas informações obrigatórias estão faltando.", + "MINLENGTHERROR": "Deve ter pelo menos {{value}} caracteres." + }, + "ID": "ID", + "EMAIL": "E-mail", + "PHONE": "Número de Telefone", + "PHONEEMPTY": "Nenhum número de telefone definido", + "PHONEVERIFIED": "Número de telefone verificado.", + "EMAILVERIFIED": "E-mail verificado", + "NOTVERIFIED": "não verificado", + "PREFERRED_LOGINNAME": "Nome de Usuário Preferencial", + "ISINITIAL": "O usuário ainda não está ativo.", + "LOGINMETHODS": { + "TITLE": "Informações de Contato", + "DESCRIPTION": "As informações fornecidas serão usadas para enviar informações importantes, como e-mails de redefinição de senha.", + "EMAIL": { + "TITLE": "E-mail", + "VALID": "validado", + "ISVERIFIED": "E-mail Verificado", + "ISVERIFIEDDESC": "Se o e-mail estiver verificado, nenhuma solicitação de verificação será feita.", + "RESEND": "Reenviar E-mail de Verificação", + "EDITTITLE": "Alterar E-mail", + "EDITDESC": "Digite o novo e-mail no campo abaixo." + }, + "PHONE": { + "TITLE": "Telefone", + "VALID": "validado", + "RESEND": "Reenviar Mensagem de Texto de Verificação", + "EDITTITLE": "Alterar Número", + "EDITVALUE": "Número de Telefone", + "EDITDESC": "Digite o novo número de telefone no campo abaixo.", + "DELETETITLE": "Excluir número de telefone", + "DELETEDESC": "Você realmente deseja excluir o número de telefone?" + }, + "RESENDCODE": "Reenviar Código", + "ENTERCODE": "Verificar", + "ENTERCODE_DESC": "Digite o código de verificação" + }, + "GRANTS": { + "TITLE": "Permissões de Usuário", + "DESCRIPTION": "Conceda a este usuário acesso a determinados projetos.", + "CREATE": { + "TITLE": "Criar Permissão de Usuário", + "DESCRIPTION": "Pesquise a organização, o projeto e as funções do projeto correspondentes." + }, + "PROJECTNAME": "Nome do Projeto", + "PROJECT-OWNED": "Projeto", + "PROJECT-GRANTED": "Projeto Concedido", + "FILTER": { + "0": "filtro por usuário", + "1": "filtro por domínio", + "2": "filtro por nome do projeto", + "3": "filtro por nome da função" + } + }, + "STATE": { + "0": "Desconhecido", + "1": "Ativo", + "2": "Inativo", + "3": "Excluído", + "4": "Bloqueado", + "5": "Suspenso", + "6": "Inicial" + }, + "SEARCH": { + "ADDITIONAL": "Nome de usuário (organização atual)", + "ADDITIONAL-EXTERNAL": "Nome de usuário (organização externa)" + }, + "TARGET": { + "SELF": "Se você deseja conceder acesso a um usuário de outra organização", + "EXTERNAL": "Para conceder acesso a um usuário de sua organização", + "CLICKHERE": "clique aqui" + }, + "SIGNEDOUT": "Você saiu. Clique no botão \"Entrar\" para fazer login novamente.", + "SIGNEDOUT_BTN": "Entrar", + "EDITACCOUNT": "Editar Conta", + "ADDACCOUNT": "Entrar Com Outra Conta", + "RESENDINITIALEMAIL": "Reenviar e-mail de ativação", + "RESENDEMAILNOTIFICATION": "Reenviar notificação por e-mail", + "TOAST": { + "CREATED": "Usuário criado com sucesso.", + "SAVED": "Perfil salvo com sucesso.", + "USERNAMECHANGED": "Nome de usuário alterado.", + "EMAILSAVED": "E-mail salvo com sucesso.", + "INITEMAILSENT": "E-mail de ativação enviado.", + "PHONESAVED": "Telefone salvo com sucesso.", + "PHONEREMOVED": "O número de telefone foi removido.", + "PHONEVERIFIED": "Número de telefone verificado com sucesso.", + "PHONEVERIFICATIONSENT": "Código de verificação de telefone enviado.", + "EMAILVERIFICATIONSENT": "Código de verificação de e-mail enviado.", + "OTPREMOVED": "OTP removido.", + "U2FREMOVED": "Fator removido.", + "PASSWORDLESSREMOVED": "Autenticação sem senha removida.", + "INITIALPASSWORDSET": "Senha inicial definida.", + "PASSWORDNOTIFICATIONSENT": "Notificação de alteração de senha enviada.", + "PASSWORDCHANGED": "Senha alterada com sucesso.", + "REACTIVATED": "Usuário reativado.", + "DEACTIVATED": "Usuário desativado.", + "SELECTEDREACTIVATED": "Usuários selecionados reativados.", + "SELECTEDDEACTIVATED": "Usuários selecionados desativados.", + "SELECTEDKEYSDELETED": "Chaves selecionadas excluídas.", + "KEYADDED": "Chave adicionada!", + "MACHINEADDED": "Usuário de serviço criado!", + "DELETED": "Usuário excluído com sucesso!", + "UNLOCKED": "Usuário desbloqueado com sucesso!", + "PASSWORDLESSREGISTRATIONSENT": "Link de registro enviado com sucesso.", + "SECRETGENERATED": "Segredo gerado com sucesso!", + "SECRETREMOVED": "Segredo removido com sucesso!" + }, + "MEMBERSHIPS": { + "TITLE": "Funções de Gerente do ZITADEL", + "DESCRIPTION": "Essas são todas as permissões de membros do usuário. Você também pode modificá-las nas páginas de detalhes da organização, projeto ou IAM.", + "ORGCONTEXT": "Você vê todas as organizações e projetos relacionados à organização atualmente selecionada.", + "USERCONTEXT": "Você vê todas as organizações e projetos para os quais está autorizado. Incluindo outras organizações.", + "CREATIONDATE": "Data de Criação", + "CHANGEDATE": "Última Modificação", + "DISPLAYNAME": "Nome de Exibição", + "REMOVE": "Remover", + "TYPE": "Tipo", + "ORGID": "ID da Organização", + "UPDATED": "A permissão de membro foi atualizada.", + "NOPERMISSIONTOEDIT": "Você não possui as permissões necessárias para editar funções!", + "TYPES": { + "UNKNOWN": "Desconhecido", + "ORG": "Organização", + "PROJECT": "Projeto", + "GRANTEDPROJECT": "Projeto Concedido" + } + }, + "PERSONALACCESSTOKEN": { + "ID": "ID", + "TOKEN": "Token", + "ADD": { + "TITLE": "Gerar Novo Token de Acesso Pessoal", + "DESCRIPTION": "Defina um tempo de expiração personalizado para o token.", + "CHOOSEEXPIRY": "Selecione uma data de expiração", + "CHOOSEDATEAFTER": "Insira uma data de expiração válida após" + }, + "ADDED": { + "TITLE": "Token de Acesso Pessoal", + "DESCRIPTION": "Certifique-se de copiar o seu token de acesso pessoal. Você não poderá vê-lo novamente!" + }, + "DELETE": { + "TITLE": "Excluir Token", + "DESCRIPTION": "Você está prestes a excluir o token de acesso pessoal. Tem certeza?" + }, + "DELETED": "Token excluído com sucesso." + } + }, + "METADATA": { + "TITLE": "Metadados", + "DESCRIPTION": "", + "KEY": "Chave", + "VALUE": "Valor", + "ADD": "Nova Entrada", + "SAVE": "Salvar", + "EMPTY": "Nenhum metadado", + "SETSUCCESS": "Elemento salvo com sucesso", + "REMOVESUCCESS": "Elemento excluído com sucesso" + }, + "FLOWS": { + "TITLE": "Ações e Fluxos", + "DESCRIPTION": "Defina scripts para executar em determinado evento.", + "ACTIONSTITLE": "Ações", + "ACTIONSDESCRIPTION": "Esses são seus scripts que você pode executar nos fluxos.", + "FLOWSTITLE": "Fluxos", + "FLOWSDESCRIPTION": "Fluxos de um determinado tipo executam ações no momento de um gatilho.", + "ID": "ID", + "NAME": "Nome", + "STATE": "Estado", + "STATES": { + "0": "sem status", + "1": "inativo", + "2": "ativo" + }, + "ADDTRIGGER": "Adicionar gatilho", + "FLOWCHANGED": "O fluxo foi alterado com sucesso", + "FLOWCLEARED": "O fluxo foi redefinido com sucesso", + "TIMEOUT": "Tempo Limite", + "TIMEOUTINSEC": "Tempo Limite em segundos", + "ALLOWEDTOFAIL": "Permitido Falhar", + "SCRIPT": "Script", + "FLOWTYPE": "Tipo de Fluxo", + "TRIGGERTYPE": "Tipo de Gatilho", + "ACTIONS": "Ações", + "ACTIONSMAX": "Com base no seu Nível, você tem um número limitado de Ações disponíveis ({{value}}). Certifique-se de desativar as que você não precisa ou considere fazer upgrade do seu nível.", + "DIALOG": { + "ADD": { + "TITLE": "Criar uma Ação" + }, + "UPDATE": { + "TITLE": "Atualizar Ação" + }, + "DELETEACTION": { + "TITLE": "Excluir Ação?", + "DESCRIPTION": "Você está prestes a excluir uma ação. Isso não pode ser desfeito. Tem certeza?", + "DELETE_SUCCESS": "Ação excluída com sucesso." + }, + "CLEAR": { + "TITLE": "Limpar fluxo?", + "DESCRIPTION": "Você está prestes a redefinir o fluxo juntamente com seus gatilhos e ações. Essa alteração não pode ser desfeita. Tem certeza?" + }, + "REMOVEACTIONSLIST": { + "TITLE": "Excluir Ações Selecionadas?", + "DESCRIPTION": "Tem certeza de que deseja excluir as ações selecionadas do fluxo?" + } + }, + "TOAST": { + "ACTIONSSET": "Ações definidas", + "ACTIONREACTIVATED": "Ações reativadas com sucesso", + "ACTIONDEACTIVATED": "Ações desativadas com sucesso" + } + }, + "IAM": { + "TITLE": "Instância", + "DESCRIPTION": "Gerencie as configurações da sua instância e organizações", + "POLICIES": { + "TITLE": "Políticas do sistema e configurações de acesso", + "DESCRIPTION": "Gerencie as políticas globais e configurações de acesso à gerência." + }, + "EVENTSTORE": { + "TITLE": "Administração de armazenamento IAM", + "DESCRIPTION": "Gerencie as visualizações do ZITADEL e eventos falhados." + }, + "MEMBER": { + "TITLE": "Gerentes", + "DESCRIPTION": "Esses gerentes têm permissão para fazer alterações na sua instância." + }, + "PAGES": { + "STATE": "Status", + "DOMAINLIST": "Domínios" + }, + "STATE": { + "0": "Não especificado", + "1": "Criando", + "2": "Executando", + "3": "Parando", + "4": "Parado" + }, + "VIEWS": { + "TITLE": "Visualizações", + "DESCRIPTION": "Este cartão mostra as visualizações do ZITADEL.", + "VIEWNAME": "Nome", + "DATABASE": "Banco de Dados", + "SEQUENCE": "Sequência", + "EVENTTIMESTAMP": "Carimbo de Data/Hora do Evento", + "LASTSPOOL": "Spool bem-sucedido", + "ACTIONS": "Ações", + "CLEAR": "Limpar", + "CLEARED": "Visualização limpa com sucesso!", + "DIALOG": { + "VIEW_CLEAR_TITLE": "Limpar Visualização", + "VIEW_CLEAR_DESCRIPTION": "Você está prestes a limpar uma visualização. A limpeza de uma visualização cria um processo durante o qual os dados possivelmente não estarão disponíveis para os usuários finais. Você tem certeza?" + } + }, + "FAILEDEVENTS": { + "TITLE": "Eventos com falha", + "DESCRIPTION": "Este cartão mostra os eventos com falha.", + "VIEWNAME": "Nome", + "DATABASE": "Banco de Dados", + "FAILEDSEQUENCE": "Sequência com falha", + "FAILURECOUNT": "Contagem de falhas", + "LASTFAILED": "Última falha em", + "ERRORMESSAGE": "Mensagem de erro", + "ACTIONS": "Ações", + "DELETE": "Remover", + "DELETESUCCESS": "Eventos com falha removidos." + }, + "EVENTS": { + "TITLE": "Eventos", + "DESCRIPTION": "Esta visualização mostra todos os eventos ocorridos.", + "EDITOR": "Editor", + "EDITORID": "ID do Editor", + "AGGREGATE": "Agregado", + "AGGREGATEID": "ID do Agregado", + "AGGREGATETYPE": "Tipo do Agregado", + "RESOURCEOWNER": "Proprietário do Recurso", + "SEQUENCE": "Sequência", + "CREATIONDATE": "Criado em", + "TYPE": "Tipo", + "PAYLOAD": "Carga", + "FILTERS": { + "BTN": "Filtrar", + "USER": { + "IDLABEL": "ID", + "CHECKBOX": "Filtrar por Editor" + }, + "AGGREGATE": { + "TYPELABEL": "Tipo de Agregado", + "IDLABEL": "ID", + "CHECKBOX": "Filtrar por Agregado" + }, + "TYPE": { + "TYPELABEL": "Tipo", + "CHECKBOX": "Filtrar por Tipo" + }, + "RESOURCEOWNER": { + "LABEL": "ID", + "CHECKBOX": "Filtrar por Proprietário do Recurso" + }, + "SEQUENCE": { + "LABEL": "Sequência", + "CHECKBOX": "Filtrar por Sequência", + "SORT": "Ordenação", + "ASC": "Crescente", + "DESC": "Decrescente" + }, + "CREATIONDATE": { + "LABEL": "Data de Criação", + "CHECKBOX": "Filtrar por Data de Criação" + }, + "OTHER": "outro", + "OTHERS": "outros" + }, + "DIALOG": { + "TITLE": "Detalhes do Evento" + } + }, + "TOAST": { + "MEMBERREMOVED": "Gerente removido.", + "MEMBERSADDED": "Gerentes adicionados.", + "MEMBERADDED": "Gerente adicionado.", + "MEMBERCHANGED": "Gerente alterado.", + "ROLEREMOVED": "Função removida.", + "ROLECHANGED": "Função alterada.", + "REACTIVATED": "Reativado", + "DEACTIVATED": "Desativado" + } + }, + "ORG": { + "PAGES": { + "NAME": "Nome", + "ID": "ID", + "CREATIONDATE": "Data de Criação", + "DATECHANGED": "Alterado", + "FILTER": "Filtro", + "FILTERPLACEHOLDER": "Filtrar pelo nome", + "LIST": "Organizações", + "LISTDESCRIPTION": "Escolha uma organização.", + "ACTIVE": "Ativa", + "CREATE": "Criar Organização", + "DEACTIVATE": "Desativar Organização", + "REACTIVATE": "Reativar Organização", + "NOPERMISSION": "Você não tem permissão para acessar as configurações da organização.", + "USERSELFACCOUNT": "Use sua conta pessoal como proprietário da organização", + "ORGDETAIL_TITLE": "Digite o nome e domínio da sua nova organização.", + "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Digite o nome da sua nova organização.", + "ORGDETAILUSER_TITLE": "Configurar Proprietário da Organização", + "DELETE": "Excluir organização", + "DEFAULTLABEL": "Padrão", + "SETASDEFAULT": "Definir como organização padrão", + "DEFAULTORGSET": "Organização padrão alterada com sucesso", + "RENAME": { + "ACTION": "Renomear", + "TITLE": "Renomear Organização", + "DESCRIPTION": "Digite o novo nome para sua organização", + "BTN": "Renomear" + }, + "ORGDOMAIN": { + "TITLE": "Verificação de Propriedade do Domínio da Organização", + "VERIFICATION": "Para verificar a propriedade do seu domínio, você precisa baixar um arquivo de verificação e enviá-lo para a URL fornecida abaixo, ou criar um registro DNS TXT para a URL fornecida. Para concluir, clique no botão para verificar.", + "VERIFICATION_SKIP": "Você pode pular a verificação por enquanto e continuar a criar sua organização, mas para usar sua organização, esta etapa deve ser concluída!", + "VERIFICATION_VALIDATION_DESC": "Os tokens são verificados regularmente para garantir que você ainda seja o proprietário do domínio.", + "VERIFICATION_NEWTOKEN_TITLE": "Solicitar Novo Token", + "VERIFICATION_NEWTOKEN_DESC": "Se você deseja solicitar um novo token, selecione seu método preferido. Se você deseja validar um token persistente, clique no botão acima.", + "VERIFICATION_VALIDATION_ONGOING": "Um token de verificação já foi solicitado. Clique no botão para iniciar uma verificação.", + "VERIFICATION_VALIDATION_ONGOING_TYPE": "Tipo do token:", + "VERIFICATION_SUCCESSFUL": "Domínio verificado com sucesso!", + "REQUESTNEWTOKEN": "Solicitar novo token", + "TYPES": { + "1": "HTTP", + "2": "DNS" + } + }, + "DOWNLOAD_FILE": "Baixar Arquivo", + "SELECTORGTOOLTIP": "Selecionar esta organização.", + "PRIMARYDOMAIN": "Domínio Principal", + "STATE": "Estado", + "USEPASSWORD": "Definir Senha Inicial", + "USEPASSWORDDESC": "O usuário não precisa definir a senha durante a inicialização." + }, + "LIST": { + "TITLE": "Organizações", + "DESCRIPTION": "Estas são as organizações na sua instância" + }, + "DOMAINS": { + "NEW": "Adicionar Domínio", + "TITLE": "Domínios", + "DESCRIPTION": "Configure seus domínios. Este domínio pode ser usado para o login dos seus usuários.", + "SETPRIMARY": "Definir como Principal", + "DELETE": { + "TITLE": "Excluir Domínio", + "DESCRIPTION": "Você está prestes a excluir um dos seus domínios. Observe que seus usuários não poderão mais usar este domínio para fazer login." + }, + "ADD": { + "TITLE": "Adicionar Domínio", + "DESCRIPTION": "Você está prestes a adicionar um domínio para sua organização. Após o processo bem-sucedido, seus usuários poderão usar o domínio para fazer login." + } + }, + "STATE": { + "0": "Não definido", + "1": "Ativa", + "2": "Desativada", + "3": "Removida" + }, + "MEMBER": { + "TITLE": "Gerentes da Organização", + "DESCRIPTION": "Defina os usuários que podem alterar as preferências da sua organização." + }, + "TOAST": { + "UPDATED": "Organização atualizada com sucesso.", + "DEACTIVATED": "Organização desativada.", + "REACTIVATED": "Organização reativada.", + "DOMAINADDED": "Domínio adicionado.", + "DOMAINREMOVED": "Domínio removido.", + "MEMBERADDED": "Gerente adicionado.", + "MEMBERREMOVED": "Gerente removido.", + "MEMBERCHANGED": "Gerente alterado.", + "SETPRIMARY": "Domínio principal definido.", + "DELETED": "Organização excluída com sucesso", + "ORG_WAS_DELETED": "Organização foi excluída." + }, + "DIALOG": { + "DEACTIVATE": { + "TITLE": "Desativar organização", + "DESCRIPTION": "Você está prestes a desativar sua organização. Os usuários não poderão fazer login posteriormente. Tem certeza de que deseja continuar?" + }, + "REACTIVATE": { + "TITLE": "Reativar organização", + "DESCRIPTION": "Você está prestes a reativar sua organização. Os usuários poderão fazer login novamente. Tem certeza de que deseja continuar?" + }, + "DELETE": { + "TITLE": "Excluir organização", + "DESCRIPTION": "Você está prestes a excluir sua organização. Isso inicia um processo no qual todos os dados relacionados à organização serão excluídos. Não será possível reverter esta ação por enquanto.", + "TYPENAME": "Digite '{{value}}' para excluir sua organização.", + "ORGNAME": "Nome", + "BTN": "Excluir" + } + } + }, + "SETTINGS": { + "INSTANCE": { + "TITLE": "Configurações da Instância", + "DESCRIPTION": "Essas configurações serão aplicadas a todas as suas organizações, a menos que tenham sido substituídas." + }, + "ORG": { + "TITLE": "Configurações da Organização", + "DESCRIPTION": "Essas configurações estendem e sobrescrevem as configurações da sua instância." + }, + "LIST": { + "GENERAL": "Geral", + "LOGIN": "Comportamento de Login e Segurança", + "LOCKOUT": "Bloqueio", + "COMPLEXITY": "Complexidade de Senha", + "NOTIFICATIONS": "Configurações de Notificação", + "NOTIFICATIONS_DESC": "Configurações de SMTP e SMS", + "MESSAGETEXTS": "Textos de Mensagem", + "IDP": "Provedores de Identidade", + "DOMAIN": "Configurações de Domínio", + "LOGINTEXTS": "Textos da Interface de Login", + "BRANDING": "Marca", + "PRIVACYPOLICY": "Política de Privacidade", + "OIDC": "Tempo de Vida e Expiração do Token OIDC", + "SECRETS": "Aparência de Segredo", + "SECURITY": "Configurações de Segurança" + }, + "GROUPS": { + "NOTIFICATIONS": "Notificações", + "LOGIN": "Login e Acesso", + "DOMAIN": "Domínio", + "TEXTS": "Textos e Idiomas", + "APPEARANCE": "Aparência", + "OTHER": "Outro" + } + }, + "SETTING": { + "DEFAULTLANGUAGE": "Idioma padrão", + "LANGUAGE": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски" + }, + "SMTP": { + "TITLE": "Configurações SMTP", + "SENDERADDRESS": "Endereço de e-mail do remetente", + "SENDERNAME": "Nome do remetente", + "HOSTANDPORT": "Host e porta", + "USER": "Usuário", + "PASSWORD": "Senha", + "SETPASSWORD": "Definir senha do SMTP", + "PASSWORDSET": "Senha do SMTP definida com sucesso.", + "TLS": "Transport Layer Security (TLS)", + "SAVED": "Salvo com sucesso!", + "REQUIREDWARN": "Para enviar notificações do seu domínio, você precisa inserir seus dados SMTP." + }, + "SMS": { + "TITLE": "Configurações de SMS", + "PROVIDERS": "Provedores", + "PROVIDER": "Provedor de SMS", + "ADDPROVIDER": "Adicionar provedor de SMS", + "ADDPROVIDERDESCRIPTION": "Escolha um dos provedores disponíveis e insira os dados necessários.", + "REMOVEPROVIDER": "Remover provedor", + "REMOVEPROVIDER_DESC": "Você está prestes a excluir uma configuração de provedor. Deseja continuar?", + "SMSPROVIDERSTATE": { + "0": "Não especificado", + "1": "Ativo", + "2": "Inativo" + }, + "ACTIVATED": "Provedor ativado.", + "DEACTIVATED": "Provedor desativado.", + "TWILIO": { + "SID": "SID", + "TOKEN": "Token", + "SENDERNUMBER": "Número do remetente", + "ADDED": "Twilio adicionado com sucesso.", + "REMOVED": "Twilio removido", + "CHANGETOKEN": "Alterar token", + "SETTOKEN": "Definir token", + "TOKENSET": "Token definido com sucesso." + } + }, + "OIDC": { + "TITLE": "Configurações OIDC", + "ACCESSTOKENLIFETIME": "Tempo de vida do token de acesso", + "IDTOKENLIFETIME": "Tempo de vida do token de ID", + "REFRESHTOKENEXPIRATION": "Expiração do token de atualização", + "REFRESHTOKENIDLEEXPIRATION": "Expiração inativa do token de atualização", + "INHOURS": "horas", + "INDAYS": "dias" + }, + "SECRETS": { + "TITLE": "Aparência de segredos", + "TYPES": "Tipos de segredos", + "TYPE": { + "1": "Email de inicialização", + "2": "Verificação de email", + "3": "Verificação de telefone", + "4": "Redefinição de senha", + "5": "Inicialização sem senha", + "6": "Segredo do aplicativo" + }, + "ADDGENERATOR": "Definir aparência de segredo", + "GENERATORTYPE": "Tipo", + "EXPIRY": "Expiração (em horas)", + "INCLUDEDIGITS": "Incluir números", + "INCLUDESYMBOLS": "Incluir símbolos", + "INCLUDELOWERLETTERS": "Incluir letras minúsculas", + "INCLUDEUPPERLETTERS": "Incluir letras maiúsculas", + "LENGTH": "Comprimento", + "UPDATED": "Configurações atualizadas." + }, + "SECURITY": { + "DESCRIPTION": "Essa configuração define o CSP para permitir o enquadramento de um conjunto de domínios permitidos. Observe que, ao permitir o uso de iFrames, você corre o risco de permitir ataques de clickjacking.", + "IFRAMEENABLED": "Permitir iFrame", + "ALLOWEDORIGINS": "URLs permitidos" + }, + "DIALOG": { + "RESET": { + "DEFAULTTITLE": "Redefinir configuração", + "DEFAULTDESCRIPTION": "Você está prestes a redefinir suas configurações para a configuração padrão da sua instância. Tem certeza de que deseja continuar?", + "LOGINPOLICY_DESCRIPTION": "Aviso: Se você continuar, as configurações do provedor de identidade também serão redefinidas para as configurações da instância." + } + } + }, + "POLICY": { + "TITLE": "Explorar configurações", + "DESCRIPTION": "Configurações pré-embaladas que melhoram sua segurança.", + "APPLIEDTO": "Aplicado a", + "PWD_COMPLEXITY": { + "TITLE": "Complexidade da senha", + "DESCRIPTION": "Garante que todas as senhas definidas correspondam a um padrão específico", + "SYMBOLANDNUMBERERROR": "Deve conter um dígito e um símbolo/ponto de pontuação.", + "SYMBOLERROR": "Deve incluir um símbolo/ponto de pontuação.", + "NUMBERERROR": "Deve incluir um dígito.", + "PATTERNERROR": "A senha não atende ao padrão exigido." + }, + "NOTIFICATION": { + "TITLE": "Notificação", + "DESCRIPTION": "Determina em quais alterações as notificações serão enviadas.", + "PASSWORDCHANGE": "Mudança de senha" + }, + "PRIVATELABELING": { + "TITLE": "Marca", + "DESCRIPTION": "Dê ao login o seu estilo personalizado e modifique seu comportamento.", + "PREVIEW_DESCRIPTION": "As alterações da política serão automaticamente aplicadas ao ambiente de visualização.", + "BTN": "Selecionar arquivo", + "ACTIVATEPREVIEW": "Aplicar configuração", + "DARK": "Modo escuro", + "LIGHT": "Modo claro", + "CHANGEVIEW": "Alterar visualização", + "ACTIVATED": "As alterações de política estão agora ATIVAS", + "THEME": "Tema", + "COLORS": "Cores", + "FONT": "Fonte", + "ADVANCEDBEHAVIOR": "Comportamento avançado", + "DROP": "Solte a imagem aqui ou", + "RELEASE": "Liberar", + "DROPFONT": "Solte o arquivo de fonte aqui", + "RELEASEFONT": "Liberar", + "USEOFLOGO": "Seu logotipo será usado no login e nos emails, enquanto o ícone é usado para elementos de IU menores, como o seletor de organização no console", + "MAXSIZE": "O tamanho máximo é limitado a 524kB", + "EMAILNOSVG": "O formato de arquivo SVG não é suportado em emails. Portanto, faça o upload do seu logotipo em PNG ou em outro formato suportado.", + "MAXSIZEEXCEEDED": "Tamanho máximo de 524kB excedido.", + "NOSVGSUPPORTED": "SVG não é suportado!", + "FONTINLOGINONLY": "A fonte está atualmente sendo exibida apenas na interface de login.", + "BACKGROUNDCOLOR": "Cor de fundo", + "PRIMARYCOLOR": "Cor primária", + "WARNCOLOR": "Cor de aviso", + "FONTCOLOR": "Cor da fonte", + "VIEWS": { + "PREVIEW": "Visualização", + "CURRENT": "Configuração atual" + }, + "PREVIEW": { + "TITLE": "Login", + "SECOND": "faça login com sua conta ZITADEL.", + "ERROR": "Usuário não encontrado!", + "PRIMARYBUTTON": "próximo", + "SECONDARYBUTTON": "registrar" + } + }, + "PWD_AGE": { + "TITLE": "Envelhecimento de senha", + "DESCRIPTION": "Você pode definir uma política para o envelhecimento de senhas. Essa política emite um aviso após o tempo de envelhecimento específico ter passado." + }, + "PWD_LOCKOUT": { + "TITLE": "Política de bloqueio", + "DESCRIPTION": "Defina um número máximo de tentativas de senha, após as quais as contas serão bloqueadas." + }, + "DOMAIN_POLICY": { + "TITLE": "Configurações de domínio" + }, + "PRIVATELABELING_POLICY": { + "TITLE": "Marca", + "BTN": "Selecionar arquivo", + "DESCRIPTION": "Personalize a aparência do login", + "ACTIVATEPREVIEW": "Ativar configuração" + }, + "LOGIN_POLICY": { + "TITLE": "Configurações de login", + "DESCRIPTION": "Defina como os usuários podem ser autenticados e configure provedores de identidade", + "DESCRIPTIONCREATEADMIN": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo.", + "DESCRIPTIONCREATEMGMT": "Os usuários podem escolher entre os provedores de identidade disponíveis abaixo. Observação: você também pode usar provedores definidos pelo sistema e provedores definidos apenas para sua organização.", + "ADVANCED": "Avançado", + "LIFETIMEDURATIONS": "Duração do login", + "SAVED": "Salvo com sucesso!" + }, + "PRIVACY_POLICY": { + "TITLE": "Política de privacidade e TOS", + "DESCRIPTION": "Defina os links para a sua Política de Privacidade e Termos de Serviço", + "TOSLINK": "Link para os Termos de Serviço", + "POLICYLINK": "Link para a Política de Privacidade", + "HELPLINK": "Link para Ajuda", + "SUPPORTEMAIL": "E-mail de suporte", + "SAVED": "Salvo com sucesso!", + "RESET_TITLE": "Restaurar valores padrão", + "RESET_DESCRIPTION": "Você está prestes a restaurar os Links padrão para TOS e Política de Privacidade. Deseja realmente continuar?" + }, + "LOGIN_TEXTS": { + "TITLE": "Textos da interface de login", + "DESCRIPTION": "Defina seus textos para as interfaces de login. Se os textos estiverem vazios, o valor padrão mostrado como marcador de posição será usado.", + "DESCRIPTION_SHORT": "Defina seus textos para as interfaces de login.", + "NEWERVERSIONEXISTS": "Existe uma versão mais recente", + "CURRENTDATE": "Configuração atual", + "CHANGEDATE": "Nova versão a partir de", + "KEYNAME": "Tela/Interface de login", + "RESET_TITLE": "Restaurar valores padrão", + "RESET_DESCRIPTION": "Você está prestes a restaurar todos os valores padrão. Todas as alterações que você fez serão excluídas permanentemente. Deseja realmente continuar?", + "UNSAVED_TITLE": "Continuar sem salvar?", + "UNSAVED_DESCRIPTION": "Você fez alterações sem salvar. Deseja salvar agora?", + "LOCALE": "Código de localidade", + "LOCALES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски" + }, + "KEYS": { + "emailVerificationDoneText": "Verificação de email concluída", + "emailVerificationText": "Verificação de email", + "externalUserNotFoundText": "Usuário externo não encontrado", + "footerText": "Rodapé", + "initMfaDoneText": "Inicialização de MFA concluída", + "initMfaOtpText": "Inicialização de MFA", + "initMfaPromptText": "Prompt de inicialização de MFA", + "initMfaU2fText": "Inicialização de Fator de Segundo Universal", + "initPasswordDoneText": "Inicialização de senha concluída", + "initPasswordText": "Inicialização de senha", + "initializeDoneText": "Inicialização de usuário concluída", + "initializeUserText": "Inicializaçãode usuário", + "linkingUserDoneText": "Vinculação de usuário concluída", + "loginText": "Login", + "logoutText": "Logout", + "mfaProvidersText": "Provedores de MFA", + "passwordChangeDoneText": "Alteração de senha concluída", + "passwordChangeText": "Alteração de senha", + "passwordResetDoneText": "Redefinição de senha concluída", + "passwordText": "Senha", + "registrationOptionText": "Opções de registro", + "registrationOrgText": "Registrar organização", + "registrationUserText": "Registrar usuário", + "selectAccountText": "Selecionar conta", + "successLoginText": "Login com sucesso", + "usernameChangeDoneText": "Alteração de nome de usuário concluída", + "usernameChangeText": "Alteração de nome de usuário", + "verifyMfaOtpText": "Verificar OTP", + "verifyMfaU2fText": "Verificar Fator de Segundo Universal", + "passwordlessPromptText": "Prompt de autenticação sem senha", + "passwordlessRegistrationDoneText": "Registro sem senha concluído", + "passwordlessRegistrationText": "Registro sem senha", + "passwordlessText": "Sem senha", + "externalRegistrationUserOverviewText": "Visão geral do usuário de registro externo" + } + }, + "MESSAGE_TEXTS": { + "TITLE": "Textos de mensagem", + "DESCRIPTION": "Defina seus textos para os emails de notificação.", + "TYPE": "Notificação", + "TYPES": { + "INIT": "Inicialização", + "VE": "Verificar Email", + "VP": "Verificar Telefone", + "PR": "Redefinição de Senha", + "DC": "Reivindicação de Domínio", + "PL": "Sem senha", + "PC": "Alteração de Senha" + }, + "CHIPS": { + "firstname": "Nome próprio", + "lastname": "Sobrenome", + "code": "Código", + "preferredLoginName": "Nome de login preferido", + "displayName": "Nome de exibição", + "nickName": "Apelido", + "loginnames": "Nomes de login", + "domain": "Domínio", + "lastEmail": "Último email", + "lastPhone": "Último telefone", + "verifiedEmail": "Email verificado", + "verifiedPhone": "Telefone verificado", + "changedate": "Data de alteração", + "username": "Nome de usuário", + "tempUsername": "Nome de usuário temporário" + }, + "TOAST": { + "UPDATED": "Textos personalizados salvos." + } + }, + "DEFAULTLABEL": "As configurações atuais correspondem ao padrão de sua Instância.", + "BTN_INSTALL": "Configurar", + "BTN_EDIT": "Modificar", + "DATA": { + "DESCRIPTION": "Descrição", + "MINLENGTH": "comprimento mínimo", + "HASNUMBER": "tem número", + "HASSYMBOL": "tem símbolo", + "HASLOWERCASE": "tem letra minúscula", + "HASUPPERCASE": "tem letra maiúscula", + "SHOWLOCKOUTFAILURES": "mostrar falhas de bloqueio", + "MAXATTEMPTS": "Máximo de tentativas de senha", + "EXPIREWARNDAYS": "Aviso de expiração após dias", + "MAXAGEDAYS": "Idade máxima em dias", + "USERLOGINMUSTBEDOMAIN": "Adicionar domínio da organização como sufixo aos nomes de login", + "USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Se você habilitar essa configuração, todos os nomes de login serão sufixados com o domínio da organização. Se essa configuração estiver desabilitada, você deve garantir que os nomes de usuário sejam exclusivos em todas as organizações.", + "VALIDATEORGDOMAINS": "Validar domínios da organização", + "SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "O endereço do remetente do SMTP corresponde ao domínio da Instância", + "ALLOWUSERNAMEPASSWORD": "Permitir usuário e senha", + "ALLOWEXTERNALIDP": "Permitir provedor de ID externo", + "ALLOWREGISTER": "Permitir registro", + "ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.", + "ALLOWEXTERNALIDP_DESC": "O login é permitido para os provedores de identidade subjacentes", + "ALLOWREGISTER_DESC": "Se a opção estiver selecionada, uma etapa adicional para registrar um usuário aparecerá no login.", + "FORCEMFA": "Forçar MFA", + "FORCEMFA_DESC": "Se a opção estiver selecionada, os usuários deverão configurar um segundo fator para o login.", + "HIDEPASSWORDRESET": "Ocultar redefinição de senha", + "HIDEPASSWORDRESET_DESC": "Se a opção estiver selecionada, o usuário não poderá redefinir sua senha no processo de login.", + "HIDELOGINNAMESUFFIX": "Ocultar sufixo do nome de login", + "HIDELOGINNAMESUFFIX_DESC": "Oculta o sufixo do nome de login na interface de login", + "IGNOREUNKNOWNUSERNAMES": "Ignorar nomes de usuário desconhecidos", + "IGNOREUNKNOWNUSERNAMES_DESC": "Se a opção estiver selecionada, a tela de senha será exibida no processo de login, mesmo se o usuário não for encontrado. O erro na verificação de senha não revelará se o nome de usuário ou a senha estavam incorretos.", + "ALLOWDOMAINDISCOVERY": "Permitir descoberta de domínio", + "ALLOWDOMAINDISCOVERY_DESC": "Se a opção estiver selecionada, o sufixo (@domain.com) de um nome de usuário desconhecido inserido na tela de login será comparado com os domínios da organização e será redirecionado para o registro dessa organização em caso de sucesso.", + "DISABLELOGINWITHEMAIL": "Desativar login com endereço de email", + "DISABLELOGINWITHPHONE": "Desativar login com número de telefone", + "DEFAULTREDIRECTURI": "URI de redirecionamento padrão", + "DEFAULTREDIRECTURI_DESC": "Define para onde o usuário será redirecionado se o login for iniciado sem um contexto de aplicativo (por exemplo, a partir do e-mail)", + "ERRORMSGPOPUP": "Mostrar Erro em Diálogo", + "DISABLEWATERMARK": "Ocultar Marca d'água", + "DISABLEWATERMARK_DESC": "Ocultar a marca d'água Powered by ZITADEL na interface de login", + "PASSWORDCHECKLIFETIME": "Vida Útil da Verificação de Senha", + "EXTERNALLOGINCHECKLIFETIME": "Vida Útil da Verificação de Login Externo", + "MFAINITSKIPLIFETIME": "Vida Útil da Inicialização de Multifator", + "SECONDFACTORCHECKLIFETIME": "Vida Útil da Verificação de Segundo Fator", + "MULTIFACTORCHECKLIFETIME": "Vida Útil da Verificação de Multifator", + "INHOURS": "horas" + }, + "RESET": "Redefinir para o padrão da instância", + "CREATECUSTOM": "Criar Política Personalizada", + "TOAST": { + "SET": "Política definida com sucesso!", + "RESETSUCCESS": "Política redefinida com sucesso!", + "UPLOADSUCCESS": "Enviado com sucesso!", + "DELETESUCCESS": "Excluído com sucesso!", + "UPLOADFAILED": "Falha no envio!" + } + }, + "ORG_DETAIL": { + "TITLE": "Organização", + "DESCRIPTION": "Aqui você pode editar a configuração da sua organização e gerenciar os membros.", + "DETAIL": { + "TITLE": "Detalhes", + "NAME": "Nome", + "DOMAIN": "Domínio", + "STATE": { + "0": "Não definido", + "1": "Ativo", + "2": "Inativo" + } + }, + "MEMBER": { + "TITLE": "Membros", + "USERNAME": "Nome de Usuário", + "DISPLAYNAME": "Nome de Exibição", + "LOGINNAME": "Nome de Login", + "EMAIL": "E-mail", + "ROLES": "Funções", + "ADD": "Adicionar Membro", + "ADDDESCRIPTION": "Insira os nomes dos usuários a serem adicionados." + }, + "TABLE": { + "TOTAL": "Total de Entradas", + "SELECTION": "Elementos Selecionados", + "DEACTIVATE": "Desativar Usuário", + "ACTIVATE": "Ativar Usuário", + "DELETE": "Excluir Usuário", + "CLEAR": "Limpar seleção" + } + }, + "PROJECT": { + "PAGES": { + "TITLE": "Projeto", + "DESCRIPTION": "Aqui você pode definir aplicativos, gerenciar funções e conceder acesso a outras organizações para usar o seu projeto.", + "DELETE": "Excluir Projeto", + "LIST": "Projetos", + "LISTDESCRIPTION": "Se você não consegue encontrar um projeto, entre em contato com o proprietário do projeto ou alguém com os direitos correspondentes para obter acesso ao projeto.", + "DETAIL": "Detalhes", + "CREATE": "Criar Projeto", + "CREATE_DESC": "Insira o nome do seu projeto.", + "ROLE": "Função", + "NOITEMS": "Nenhum projeto", + "ZITADELPROJECT": "Isto pertence ao projeto ZITADEL. Atenção: Se você fizer alterações, o ZITADEL pode não funcionar como o esperado.", + "TYPE": { + "OWNED": "Projetos Próprios", + "GRANTED": "Projetos Concedidos", + "OWNED_SINGULAR": "Projeto Próprio", + "GRANTED_SINGULAR": "Projeto Concedido" + }, + "PRIVATELABEL": { + "TITLE": "Configuração de Marca", + "0": { + "TITLE": "Não Especificado", + "DESC": "Assim que o usuário for identificado, a marca da organização do usuário identificado será exibida antes da marca padrão do sistema." + }, + "1": { + "TITLE": "Usar configuração do projeto", + "DESC": "Será exibida a marca da organização que possui o projeto" + }, + "2": { + "TITLE": "Usar configuração da organização do usuário", + "DESC": "Será exibida a marca da organização do projeto, mas assim que o usuário for identificado, será exibida a configuração da organização do usuário identificado." + }, + "DIALOG": { + "TITLE": "Configuração de Marca", + "DESCRIPTION": "Selecione o comportamento do login ao usar o projeto." + } + }, + "PINNED": "Fixado", + "ALL": "Todos", + "CREATEDON": "Criado em", + "LASTMODIFIED": "Última modificação em", + "ADDNEW": "Criar Novo Projeto", + "DIALOG": { + "REACTIVATE": { + "TITLE": "Reativar Projeto", + "DESCRIPTION": "Tem certeza de que deseja reativar o seu projeto?" + }, + "DEACTIVATE": { + "TITLE": "Desativar Projeto", + "DESCRIPTION": "Tem certeza de que deseja desativar o seu projeto?" + }, + "DELETE": { + "TITLE": "Excluir Projeto", + "DESCRIPTION": "Tem certeza de que deseja excluir o seu projeto?", + "TYPENAME": "Digite o nome do projeto para excluí-lo permanentemente." + } + } + }, + "SETTINGS": { + "TITLE": "Configurações", + "DESCRIPTION": "" + }, + "STATE": { + "TITLE": "Status", + "0": "Não definido", + "1": "Ativo", + "2": "Inativo" + }, + "TYPE": { + "TITLE": "Tipo", + "0": "Tipo desconhecido", + "1": "Próprio", + "2": "Concedido" + }, + "NAME": "Nome", + "NAMEDIALOG": { + "TITLE": "Renomear Projeto", + "DESCRIPTION": "Digite o novo nome para o seu projeto", + "NAME": "Novo Nome" + }, + "MEMBER": { + "TITLE": "Gerentes", + "TITLEDESC": "Os gerentes podem fazer alterações neste projeto com base em sua função.", + "DESCRIPTION": "Esses gerentes podem ter permissão para editar o seu projeto.", + "USERNAME": "Nome de Usuário", + "DISPLAYNAME": "Nome de Exibição", + "LOGINNAME": "Nome de Login", + "EMAIL": "E-mail", + "ROLES": "Funções", + "USERID": "ID do Usuário" + }, + "GRANT": { + "EMPTY": "Nenhuma organização concedida.", + "TITLE": "Concessões de Projeto", + "DESCRIPTION": "Permite que outra organização use o seu projeto.", + "EDITTITLE": "Editar funções", + "CREATE": { + "TITLE": "Criar Concessão de Organização", + "SEL_USERS": "Selecione os usuários aos quais deseja conceder acesso", + "SEL_PROJECT": "Buscar um projeto", + "SEL_ROLES": "Selecione as funções que deseja adicionar à concessão", + "SEL_USER": "Selecione usuários", + "SEL_ORG": "Definir o domínio", + "SEL_ORG_DESC": "Insira o domínio completo para especificar a organização a ser concedida.", + "ORG_TITLE": "Organização", + "ORG_DESCRIPTION": "Você está prestes a conceder um usuário para a organização {{name}}.", + "ORG_DESCRIPTION_DESC": "Altere o contexto no cabeçalho acima para conceder um usuário para outra organização.", + "SEL_ORG_FORMFIELD": "Domínio Completo", + "SEL_ORG_BUTTON": "Buscar Organização", + "FOR_ORG": "A concessão é criada para:" + }, + "DETAIL": { + "TITLE": "Concessão de Projeto", + "DESC": "Você pode selecionar quais funções podem ser usadas pela organização especificada e eleger gerentes", + "MEMBERTITLE": "Gerentes", + "MEMBERDESC": "Estes são os gerentes da organização concedida. Adicione usuários aqui que devem ter acesso para editar os dados do projeto.", + "PROJECTNAME": "Nome do Projeto", + "GRANTEDORG": "Organização Concedida", + "RESOURCEOWNER": "Proprietário do Recurso" + }, + "STATE": "Status", + "STATES": { + "1": "Ativo", + "2": "Inativo" + }, + "ALL": "Todos", + "SHOWDETAIL": "Mostrar Detalhes", + "USER": "Usuário", + "MEMBERS": "Gerentes", + "ORG": "Organização", + "PROJECTNAME": "Nome do Projeto", + "GRANTEDORG": "Organização Concedida", + "GRANTEDORGDOMAIN": "Domínio", + "RESOURCEOWNER": "Proprietário do Recurso", + "GRANTEDORGNAME": "Nome da Organização", + "GRANTID": "ID da Concessão", + "CREATIONDATE": "Data de Criação", + "CHANGEDATE": "Última modificação", + "DATES": "Datas", + "ROLENAMESLIST": "Funções", + "NOROLES": "Nenhuma função", + "TYPE": "Tipo", + "TOAST": { + "PROJECTGRANTUSERGRANTADDED": "Concessão de projeto criada.", + "PROJECTGRANTADDED": "Concessão de projeto criada.", + "PROJECTGRANTCHANGED": "Concessão de projeto alterada.", + "PROJECTGRANTMEMBERADDED": "Gerente da concessão adicionado.", + "PROJECTGRANTMEMBERCHANGED": "Gerente da concessão alterado.", + "PROJECTGRANTMEMBERREMOVED": "Gerente da concessão removido.", + "PROJECTGRANTUPDATED": "Concessão de Projeto atualizada" + }, + "DIALOG": { + "DELETE_TITLE": "Excluir concessão de projeto", + "DELETE_DESCRIPTION": "Você está prestes a excluir uma concessão de projeto. Tem certeza?" + }, + "ROLES": "Funções do Projeto" + }, + "APP": { + "TITLE": "Aplicativos", + "NAME": "Nome", + "NAMEREQUIRED": "É necessário um nome." + }, + "ROLE": { + "EMPTY": "Nenhuma função foi criada ainda.", + "ADDNEWLINE": "Adicionar função adicional", + "KEY": "Chave", + "TITLE": "Funções", + "DESCRIPTION": "Defina algumas funções que podem ser usadas para criar concessões de projeto.", + "NAME": "Nome", + "DISPLAY_NAME": "Nome de Exibição", + "GROUP": "Grupo", + "ACTIONS": "Ações", + "ADDTITLE": "Criar Função", + "ADDDESCRIPTION": "Insira os dados para a nova função.", + "EDITTITLE": "Editar Função", + "EDITDESCRIPTION": "Insira os novos dados para a função.", + "DELETE": "Excluir Função", + "CREATIONDATE": "Criada em", + "CHANGEDATE": "Última modificação", + "SELECTGROUPTOOLTIP": "Selecione todas as funções do grupo {{group}}.", + "OPTIONS": "Opções", + "ASSERTION": "Afirma Funções na Autenticação", + "ASSERTION_DESCRIPTION": "As informações de função são enviadas pelo ponto de extremidade Userinfo e dependendo das configurações do seu aplicativo em tokens e outros tipos.", + "CHECK": "Verificar autorização na Autenticação", + "CHECK_DESCRIPTION": "Se definido, os usuários só podem fazer login se alguma função for atribuída à sua conta.", + "DIALOG": { + "DELETE_TITLE": "Excluir função", + "DELETE_DESCRIPTION": "Você está prestes a excluir uma função de projeto. Tem certeza?" + } + }, + "HAS_PROJECT": "Verificar Projeto na Autenticação", + "HAS_PROJECT_DESCRIPTION": "Verifica se a organização dousuário possui este projeto. Se não possuir, o usuário não poderá ser autenticado.", + "TABLE": { + "TOTAL": "Total de Entradas:", + "SELECTION": "Elementos Selecionados", + "DEACTIVATE": "Desativar Projeto", + "ACTIVATE": "Ativar Projeto", + "DELETE": "Excluir Projeto", + "ORGNAME": "Nome da Organização", + "ORGDOMAIN": "Domínio da Organização", + "STATE": "Status", + "TYPE": "Tipo", + "CREATIONDATE": "Criado em", + "CHANGEDATE": "Última modificação", + "RESOURCEOWNER": "Proprietário do Recurso", + "SHOWTABLE": "Mostrar tabela", + "SHOWGRID": "Mostrar grade", + "EMPTY": "Nenhum projeto encontrado" + }, + "TOAST": { + "MEMBERREMOVED": "Gerente removido.", + "MEMBERSADDED": "Gerentes adicionados.", + "MEMBERADDED": "Gerente adicionado.", + "MEMBERCHANGED": "Gerente alterado.", + "ROLESCREATED": "Funções criadas.", + "ROLEREMOVED": "Função removida.", + "ROLECHANGED": "Função alterada.", + "REACTIVATED": "Reativado.", + "DEACTIVATED": "Desativado.", + "CREATED": "Projeto criado.", + "UPDATED": "Projeto alterado.", + "GRANTUPDATED": "Concessão alterada.", + "DELETED": "Projeto excluído." + } + }, + "ROLES": { + "DIALOG": { + "DELETE_TITLE": "Excluir função", + "DELETE_DESCRIPTION": "Você está prestes a excluir uma função. Tem certeza?" + } + }, + "NEXTSTEPS": { + "TITLE": "Próximos Passos" + }, + "IDP": { + "LIST": { + "TITLE": "Provedores de Identidade", + "DESCRIPTION": "Gerencie a configuração do seu Provedor de Identidade, que pode ser ativado nas suas Configurações de Login.", + "ACTIVETITLE": "Provedores de Identidade Ativos" + }, + "CREATE": { + "TITLE": "Adicionar provedor", + "DESCRIPTION": "Selecione um ou mais dos seguintes provedores.", + "STEPPERTITLE": "Criar Provedor", + "OIDC": { + "TITLE": "Provedor OIDC", + "DESCRIPTION": "Insira os dados necessários para o seu provedor OIDC." + }, + "OAUTH": { + "TITLE": "Provedor OAuth", + "DESCRIPTION": "Insira os dados necessários para o seu provedor OAuth." + }, + "JWT": { + "TITLE": "Provedor JWT", + "DESCRIPTION": "Insira os dados necessários para o seu provedor JWT." + }, + "GOOGLE": { + "TITLE": "Provedor Google", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Google" + }, + "GITLAB": { + "TITLE": "Provedor Gitlab", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Gitlab" + }, + "GITLABSELFHOSTED": { + "TITLE": "Provedor Gitlab Self Hosted", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Gitlab Self Hosted" + }, + "GITHUBES": { + "TITLE": "Provedor GitHub Enterprise Server", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do GitHub Enterprise Server" + }, + "GITHUB": { + "TITLE": "Provedor Github", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade do Github" + }, + "AZUREAD": { + "TITLE": "Provedor Microsoft", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade da Microsoft" + }, + "LDAP": { + "TITLE": "Active Directory / LDAP", + "DESCRIPTION": "Insira as credenciais para o seu Provedor de Identidade LDAP" + } + }, + "DETAIL": { + "TITLE": "Provedor de Identidade", + "DESCRIPTION": "Atualize a configuração do seu provedor", + "DATECREATED": "Criado em", + "DATECHANGED": "Última modificação" + }, + "OPTIONS": { + "ISAUTOCREATION": "Criação Automática", + "ISAUTOCREATION_DESC": "Se selecionado, uma conta será criada se ainda não existir.", + "ISAUTOUPDATE": "Atualização Automática", + "ISAUTOUPDATE_DESC": "Se selecionado, as contas são atualizadas ao serem reautenticadas.", + "ISCREATIONALLOWED": "Criação de Conta Permitida", + "ISCREATIONALLOWED_DESC": "Determina se as contas podem ser criadas.", + "ISLINKINGALLOWED": "Vinculação de Conta Permitida", + "ISLINKINGALLOWED_DESC": "Determina se uma identidade pode ser vinculada a uma conta existente." + }, + "OWNERTYPES": { + "0": "desconhecido", + "1": "Instância", + "2": "Organização" + }, + "STATES": { + "1": "ativo", + "2": "inativo" + }, + "AZUREADTENANTTYPES": { + "3": "ID do Locatário", + "0": "Comum", + "1": "Organizações", + "2": "Consumidores" + }, + "AZUREADTENANTTYPE": "Tipo de Locatário", + "AZUREADTENANTID": "ID do Locatário", + "EMAILVERIFIED": "E-mail Verificado", + "NAMEHINT": "Se especificado, será mostrado na interface de login.", + "OPTIONAL": "opcional", + "LDAPATTRIBUTES": "Atributos LDAP", + "UPDATEBINDPASSWORD": "atualizar Senha de Vinculação", + "UPDATECLIENTSECRET": "atualizar segredo do cliente", + "ADD": "Adicionar Provedor de Identidade", + "TYPE": "Tipo", + "OWNER": "Proprietário", + "ID": "ID", + "NAME": "Nome", + "AUTHORIZATIONENDPOINT": "Ponto de Autorização", + "TOKENENDPOINT": "Ponto de Token", + "USERENDPOINT": "Ponto de Usuário", + "IDATTRIBUTE": "Atributo ID", + "AVAILABILITY": "Disponibilidade", + "AVAILABLE": "disponível", + "AVAILABLEBUTINACTIVE": "disponível, mas inativo", + "SETAVAILABLE": "definir como disponível", + "SETUNAVAILABLE": "definir como indisponível", + "CONFIG": "Configuração", + "STATE": "Status", + "ISSUER": "Eu emissor", + "SCOPESLIST": "Lista de Escopos", + "CLIENTID": "ID do Cliente", + "CLIENTSECRET": "Segredo do Cliente", + "LDAPCONNECTION": "Conexão", + "LDAPUSERBINDING": "Vínculo de Usuário", + "BASEDN": "BaseDn", + "BINDDN": "BindDn", + "BINDPASSWORD": "Senha de Vinculação", + "SERVERS": "Servidores", + "STARTTLS": "Iniciar TLS", + "TIMEOUT": "Tempo limite em segundos", + "USERBASE": "Base de Usuários", + "USERFILTERS": "Filtros de Usuário", + "USEROBJECTCLASSES": "Classes de Objeto de Usuário", + "REQUIRED": "obrigatório", + "LDAPIDATTRIBUTE": "atributo ID", + "AVATARURLATTRIBUTE": "atributo URL de Avatar", + "DISPLAYNAMEATTRIBUTE": "atributo de Nome Exibido", + "EMAILATTRIBUTEATTRIBUTE": "atributo de E-mail", + "EMAILVERIFIEDATTRIBUTE": "atributo de E-mail Verificado", + "FIRSTNAMEATTRIBUTE": "atributo de Nome Próprio", + "LASTNAMEATTRIBUTE": "atributo de Sobrenome", + "NICKNAMEATTRIBUTE": "atributo de Apelido", + "PHONEATTRIBUTE": "atributo de Telefone", + "PHONEVERIFIEDATTRIBUTE": "atributo de Telefone Verificado", + "PREFERREDLANGUAGEATTRIBUTE": "atributo de Idioma Preferido", + "PREFERREDUSERNAMEATTRIBUTE": "atributo de Nome de Usuário Preferido", + "PROFILEATTRIBUTE": "atributo de Perfil", + "IDPDISPLAYNAMMAPPING": "Mapeamento de Nome de Exibição do IDP", + "USERNAMEMAPPING": "Mapeamento de Nome de Usuário", + "DATES": "Datas", + "CREATIONDATE": "Criado em", + "CHANGEDATE": "Última Modificação", + "DEACTIVATE": "Desativar", + "ACTIVATE": "Ativar", + "DELETE": "Excluir", + "DELETE_TITLE": "Excluir Provedor de Identidade", + "DELETE_DESCRIPTION": "Você está prestes a excluir um provedor de identidade. As alterações resultantes são irreversíveis. Você realmente deseja fazer isso?", + "DELETE_SELECTION_TITLE": "Excluir Provedor de Identidade", + "DELETE_SELECTION_DESCRIPTION": "Você está prestes a excluir um provedor de identidade. As alterações resultantes são irreversíveis. Você realmente deseja fazer isso?", + "EMPTY": "Nenhum provedor de identidade disponível", + "OIDC": { + "GENERAL": "Informações Gerais", + "TITLE": "Configuração OIDC", + "DESCRIPTION": "Insira os dados para o Provedor de Identidade OIDC." + }, + "JWT": { + "TITLE": "Configuração JWT", + "DESCRIPTION": "Insira os dados para o Provedor de Identidade JWT.", + "HEADERNAME": "Nome do Cabeçalho", + "JWTENDPOINT": "Endpoint JWT", + "JWTKEYSENDPOINT": "Endpoint de Chaves JWT" + }, + "TOAST": { + "SAVED": "Salvo com sucesso.", + "REACTIVATED": "Provedor de identidade reativado.", + "DEACTIVATED": "Provedor de identidade desativado.", + "SELECTEDREACTIVATED": "Provedores de identidade selecionados reativados.", + "SELECTEDDEACTIVATED": "Provedores de identidade selecionados desativados.", + "SELECTEDKEYSDELETED": "Provedores de identidade selecionados excluídos.", + "DELETED": "Provedor de identidade removido com sucesso!", + "ADDED": "Adicionado com sucesso.", + "REMOVED": "Removido com sucesso." + } + }, + "MFA": { + "LIST": { + "MULTIFACTORTITLE": "Sem Senha", + "MULTIFACTORDESCRIPTION": "Defina seus Multifactores para autenticação sem senha aqui.", + "SECONDFACTORTITLE": "Autenticação Multifator", + "SECONDFACTORDESCRIPTION": "Defina outros fatores possíveis com os quais você pode proteger a autenticação por senha." + }, + "CREATE": { + "TITLE": "Novo Fator", + "DESCRIPTION": "Selecione o tipo do novo fator." + }, + "DELETE": { + "TITLE": "Excluir Fator", + "DESCRIPTION": "Você está prestes a excluir um fator das Configurações de Login. Tem certeza?" + }, + "TOAST": { + "ADDED": "Adicionado com sucesso.", + "SAVED": "Salvo com sucesso.", + "DELETED": "Removido com sucesso" + }, + "TYPE": "Tipo", + "MULTIFACTORTYPES": { + "0": "Desconhecido", + "1": "Impressão Digital, Chaves de Segurança, Face ID e outros" + }, + "SECONDFACTORTYPES": { + "0": "Desconhecido", + "1": "Senha de Uso Único (OTP)", + "2": "Impressão Digital, Chaves de Segurança, Face ID e outros" + } + }, + "LOGINPOLICY": { + "CREATE": { + "TITLE": "Configurações de Login", + "DESCRIPTION": "Defina como seus usuários podem ser autenticados na sua organização." + }, + "IDPS": "Provedores de Identidade", + "ADDIDP": { + "TITLE": "Adicionar Provedor de Identidade", + "DESCRIPTION": "Você pode selecionar provedores pré-definidos ou criados por você para autenticação.", + "SELECTIDPS": "Provedores de identidade" + }, + "PASSWORDLESS": "Login sem Senha", + "PASSWORDLESSTYPE": { + "0": "Não permitido", + "1": "Permitido" + } + }, + "APP": { + "LIST": "Aplicações", + "COMPLIANCE": "Conformidade OIDC", + "URLS": "URLs", + "CONFIGURATION": "Configuração", + "TOKEN": "Configurações de Token", + "PAGES": { + "TITLE": "Aplicativo", + "ID": "ID", + "DESCRIPTION": "Aqui você pode editar os dados do seu aplicativo e sua configuração.", + "CREATE": "Criar aplicativo", + "CREATE_SELECT_PROJECT": "Selecione o seu projeto primeiro", + "CREATE_NEW_PROJECT": "ou crie um novo aqui.", + "CREATE_DESC_TITLE": "Insira os Detalhes do seu Aplicativo Passo a Passo", + "CREATE_DESC_SUB": "Uma configuração recomendada será gerada automaticamente.", + "STATE": "Status", + "DATECREATED": "Criado", + "DATECHANGED": "Alterado", + "URLS": "URLs", + "DELETE": "Excluir App", + "DETAIL": { + "TITLE": "Detalhe", + "STATE": { + "0": "Não definido", + "1": "Ativo", + "2": "Inativo" + } + }, + "DIALOG": { + "CONFIG": { + "TITLE": "Alterar Configuração OIDC" + }, + "DELETE": { + "TITLE": "Excluir App", + "DESCRIPTION": "Você realmente deseja excluir este aplicativo?" + } + }, + "NEXTSTEPS": { + "TITLE": "Próximos Passos", + "0": { + "TITLE": "Adicionar funções", + "DESC": "Insira as funções do seu projeto" + }, + "1": { + "TITLE": "Adicionar usuários", + "DESC": "Adicione novos usuários da sua organização" + }, + "2": { + "TITLE": "Ajuda e Suporte", + "DESC": "Leia nossa documentação sobre como criar aplicativos ou entre em contato com nosso suporte" + } + } + }, + "NAMEDIALOG": { + "TITLE": "Renomear Aplicativo", + "DESCRIPTION": "Digite o novo nome para o seu aplicativo", + "NAME": "Novo Nome" + }, + "NAME": "Nome", + "TYPE": "Tipo de Aplicativo", + "AUTHMETHOD": "Método de Autenticação", + "AUTHMETHODSECTION": "Método de Autenticação", + "GRANT": "Tipos de Concessão", + "ADDITIONALORIGINS": "Origens Adicionais", + "ADDITIONALORIGINSDESC": "Se você deseja adicionar origens adicionais ao seu aplicativo que não são usadas como redirecionamento, você pode fazer isso aqui.", + "ORIGINS": "Origens", + "NOTANORIGIN": "O valor inserido não é uma origem", + "PROSWITCH": "Sou um profissional. Pular este assistente.", + "NAMEANDTYPESECTION": "Nome e Tipo", + "TITLEFIRST": "Nome do aplicativo", + "TYPETITLE": "Tipo de aplicativo", + "OIDC": { + "WELLKNOWN": "Links adicionais podem ser obtidos a partir do endpoint de descoberta.", + "INFO": { + "ISSUER": "Emissor", + "CLIENTID": "ID do Cliente" + }, + "CURRENT": "Configuração Atual", + "TOKENSECTIONTITLE": "Opções de Token de Autenticação", + "REDIRECTSECTIONTITLE": "Configurações de Redirecionamento", + "REDIRECTTITLE": "Especifique as URIs para onde o login será redirecionado.", + "POSTREDIRECTTITLE": "Esta é a URI de redirecionamento após o logout.", + "REDIRECTDESCRIPTIONWEB": "URIs de redirecionamento devem começar com https://. http:// é válido apenas com o modo de desenvolvimento habilitado.", + "REDIRECTDESCRIPTIONNATIVE": "URIs de redirecionamento devem começar com seu próprio protocolo, http://127.0.0.1, http://[::1] ou http://localhost.", + "REDIRECTNOTVALID": "Esta URI de redirecionamento não é válida.", + "COMMAORENTERSEPERATION": "separar com ↵", + "TYPEREQUIRED": "O tipo é obrigatório.", + "TITLE": "Configuração OIDC", + "CLIENTID": "ID do Cliente", + "CLIENTSECRET": "Segredo do Cliente", + "CLIENTSECRET_NOSECRET": "Com o fluxo de autenticação escolhido, nenhum segredo é necessário e, portanto, não está disponível.", + "CLIENTSECRET_DESCRIPTION": "Mantenha o segredo do cliente em um local seguro, pois ele desaparecerá assim que o diálogo for fechado.", + "REGENERATESECRET": "Regenerar Segredo do Cliente", + "DEVMODE": "Modo de Desenvolvimento", + "DEVMODEDESC": "Atenção: Com o modo de desenvolvimento habilitado, as URIs de redirecionamento não serão validadas.", + "SKIPNATIVEAPPSUCCESSPAGE": "Pular Página de Sucesso de Login", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Pule a página de sucesso após o login para este aplicativo nativo.", + "REDIRECT": "URIs de Redirecionamento", + "REDIRECTSECTION": "URIs de Redirecionamento", + "POSTLOGOUTREDIRECT": "URIs de Pós-Logout", + "RESPONSESECTION": "Tipos de Resposta", + "GRANTSECTION": "Tipos de Concessão", + "GRANTTITLE": "Selecione os tipos de concessão. Observação: O tipo Implícito está disponível apenas para aplicativos baseados em navegador.", + "APPTYPE": { + "0": "Web", + "1": "Agente de Usuário", + "2": "Nativo" + }, + "RESPONSETYPE": "Tipos de Resposta", + "RESPONSE": { + "0": "Código", + "1": "ID Token", + "2": "Token-ID Token" + }, + "REFRESHTOKEN": "Token de Atualização", + "GRANTTYPE": "Tipos de Concessão", + "GRANT": { + "0": "Código de Autorização", + "1": "Implícito", + "2": "Token de Atualização", + "3": "Código do Dispositivo" + }, + "AUTHMETHOD": { + "0": "Básico", + "1": "Post", + "2": "Nenhum", + "3": "JWT de Chave Privada" + }, + "TOKENTYPE": "Tipo de Token de Autenticação", + "TOKENTYPE0": "Token de Portador", + "TOKENTYPE1": "JWT", + "UNSECUREREDIRECT": "Espero que você saiba o que está fazendo.", + "OVERVIEWSECTION": "Visão Geral", + "OVERVIEWTITLE": "Agora você terminou. Revise sua configuração.", + "ACCESSTOKENROLEASSERTION": "Adicionar funções do usuário ao token de acesso", + "ACCESSTOKENROLEASSERTION_DESCRIPTION": "Se selecionado, as funções solicitadas do usuário autenticado serão adicionadas ao token de acesso.", + "IDTOKENROLEASSERTION": "Funções do usuário no ID Token", + "IDTOKENROLEASSERTION_DESCRIPTION": "Se selecionado, as funções solicitadas do usuário autenticado serão adicionadas ao ID token.", + "IDTOKENUSERINFOASSERTION": "Informações do usuário no ID Token", + "IDTOKENUSERINFOASSERTION_DESCRIPTION": "Permite que os clientes obtenham claims de perfil, e-mail, telefone e endereço do ID token.", + "CLOCKSKEW": "Permite que os clientes lidem com a discrepância de tempo do OP e do cliente. A duração (0-5s) será adicionada ao claim exp e subtraída de iat, auth_time e nbf.", + "RECOMMENDED": "recomendado", + "NOTRECOMMENDED": "não recomendado", + "SELECTION": { + "APPTYPE": { + "WEB": { + "TITLE": "Web", + "DESCRIPTION": "Aplicações da Web regulares como .net, PHP, Node.js, Java, etc." + }, + "NATIVE": { + "TITLE": "Nativo", + "DESCRIPTION": "Aplicativos móveis, desktop, dispositivos inteligentes, etc." + }, + "USERAGENT": { + "TITLE": "Agente de Usuário", + "DESCRIPTION": "Aplicativos de página única (SPA) e, em geral, todos os frameworks JS executados em navegadores" + } + } + } + }, + "API": { + "INFO": { + "CLIENTID": "ID do Cliente" + }, + "REGENERATESECRET": "Regenerar Segredo do Cliente", + "SELECTION": { + "TITLE": "API", + "DESCRIPTION": "APIs em geral" + }, + "AUTHMETHOD": { + "0": "Básico", + "1": "JWT de Chave Privada" + } + }, + "SAML": { + "SELECTION": { + "TITLE": "SAML", + "DESCRIPTION": "Aplicativos SAML" + }, + "CONFIGSECTION": "Configuração SAML", + "URL": "URL onde o arquivo de Metadados está localizado", + "OR": "ou", + "XML": "Carregar XML de Metadados", + "METADATA": "Metadados", + "METADATAFROMFILE": "Metadados do Arquivo" + }, + "AUTHMETHODS": { + "CODE": { + "TITLE": "Código", + "DESCRIPTION": "Trocar o código de autorização pelos tokens" + }, + "PKCE": { + "TITLE": "PKCE", + "DESCRIPTION": "Usar um hash aleatório em vez de um segredo de cliente estático para maior segurança" + }, + "POST": { + "TITLE": "POST", + "DESCRIPTION": "Enviar client_id e client_secret como parte do formulário" + }, + "PK_JWT": { + "TITLE": "JWT de Chave Privada", + "DESCRIPTION": "Usar uma chave privada para autorizar seu aplicativo" + }, + "BASIC": { + "TITLE": "Básico", + "DESCRIPTION": "Autenticação com Nome de Usuário e Senha" + }, + "IMPLICIT": { + "TITLE": "Implícito", + "DESCRIPTION": "Obter os tokens diretamente do ponto de extremidade de autorização" + }, + "DEVICECODE": { + "TITLE": "Código do Dispositivo", + "DESCRIPTION": "Autorizar o dispositivo em um computador ou smartphone." + }, + "CUSTOM": { + "TITLE": "Personalizado", + "DESCRIPTION": "Sua configuração não corresponde a nenhuma outra opção." + } + }, + "TOAST": { + "REACTIVATED": "Aplicativo reativado.", + "DEACTIVATED": "Aplicativo desativado.", + "OIDCUPDATED": "Aplicativo atualizado.", + "APIUPDATED": "Aplicativo atualizado.", + "UPDATED": "Aplicativo atualizado.", + "CREATED": "Aplicativo criado.", + "CLIENTSECRETREGENERATED": "segredo do cliente gerado.", + "DELETED": "Aplicativo excluído.", + "CONFIGCHANGED": "Alterações detectadas!" + } + }, + "GENDERS": { + "0": "Desconhecido", + "1": "Feminino", + "2": "Masculino", + "3": "Outro" + }, + "LANGUAGES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски" + }, + "MEMBER": { + "ADD": "Adicionar um Gerente", + "CREATIONTYPE": "Tipo de Criação", + "CREATIONTYPES": { + "3": "IAM", + "2": "Organização", + "0": "Projeto Próprio", + "1": "Projeto Concedido", + "4": "Projeto" + }, + "EDITROLE": "Editar funções", + "EDITFOR": "Editar as funções para o usuário: {{value}}", + "DIALOG": { + "DELETE_TITLE": "Remover Gerente", + "DELETE_DESCRIPTION": "Você está prestes a remover um gerente. Tem certeza?" + } + }, + "ROLESLABEL": "Funções", + "GRANTS": { + "TITLE": "Autorizações", + "DESC": "Estas são todas as autorizações na sua organização.", + "DELETE": "Excluir Autorização", + "EMPTY": "Nenhuma autorização encontrada", + "ADD": "Criar Autorização", + "ADD_BTN": "Novo", + "PROJECT": { + "TITLE": "Autorização", + "DESCRIPTION": "Defina as autorizações para o projeto especificado. Observe que você só pode ver entradas de projetos e usuários para os quais você tem permissão." + }, + "USER": { + "TITLE": "Autorização", + "DESCRIPTION": "Defina as autorizações para o usuário especificado. Observe que você só pode ver entradas de projetos e usuários para os quais você tem permissão." + }, + "CREATE": { + "TITLE": "Criar autorização", + "DESCRIPTION": "Pesquise a organização, o projeto e as funções correspondentes." + }, + "EDIT": { + "TITLE": "Alterar autorização" + }, + "DETAIL": { + "TITLE": "Detalhes da Autorização", + "DESCRIPTION": "Aqui você pode ver todos os detalhes da autorização." + }, + "TOAST": { + "UPDATED": "Autorização atualizada.", + "REMOVED": "Autorização removida", + "BULKREMOVED": "Autorizações removidas." + }, + "DIALOG": { + "DELETE_TITLE": "Excluir autorização", + "DELETE_DESCRIPTION": "Você está prestesa excluir uma autorização. Deseja continuar?", + "BULK_DELETE_TITLE": "Excluir autorizações", + "BULK_DELETE_DESCRIPTION": "Você está prestes a excluir várias autorizações. Deseja continuar?" + } + }, + "CHANGES": { + "LISTTITLE": "Últimas Alterações", + "BOTTOM": "Você chegou ao final da lista.", + "LOADMORE": "Carregar mais", + "ORG": { + "TITLE": "Atividade", + "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração na organização." + }, + "PROJECT": { + "TITLE": "Atividade", + "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração no projeto." + }, + "USER": { + "TITLE": "Atividade", + "DESCRIPTION": "Aqui você pode ver os eventos mais recentes que geraram uma alteração no usuário." + } + } +} diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index a9527fb16..423fbf027 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1041,7 +1041,7 @@ "pl": "Polski", "zh": "简体中文", "bg": "Български", - "pt": "Portuguese", + "pt": "Portuguese", "mk": "Македонски" }, "SMTP": { From 1b44e9ad7620d7766a1a09ce9d7b532eecb96763 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 21 Jul 2023 13:04:55 +0200 Subject: [PATCH 27/76] fix: user grant by id (#6242) (cherry picked from commit cd5e176e3022b469a1759d78ef1267da3a283ab9) --- internal/api/grpc/management/user_grant.go | 2 +- internal/query/user_grant.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/api/grpc/management/user_grant.go b/internal/api/grpc/management/user_grant.go index a9beb328c..7dc4a1e58 100644 --- a/internal/api/grpc/management/user_grant.go +++ b/internal/api/grpc/management/user_grant.go @@ -11,7 +11,7 @@ import ( ) func (s *Server) GetUserGrantByID(ctx context.Context, req *mgmt_pb.GetUserGrantByIDRequest) (*mgmt_pb.GetUserGrantByIDResponse, error) { - idQuery, err := query.NewUserGrantGrantIDSearchQuery(req.GrantId) + idQuery, err := query.NewUserGrantIDSearchQuery(req.GrantId) if err != nil { return nil, err } diff --git a/internal/query/user_grant.go b/internal/query/user_grant.go index 6d0eaa95b..f78cd99d8 100644 --- a/internal/query/user_grant.go +++ b/internal/query/user_grant.go @@ -93,6 +93,10 @@ func NewUserGrantGrantIDSearchQuery(id string) (SearchQuery, error) { return NewTextQuery(UserGrantGrantID, id, TextEquals) } +func NewUserGrantIDSearchQuery(id string) (SearchQuery, error) { + return NewTextQuery(UserGrantID, id, TextEquals) +} + func NewUserGrantUserTypeQuery(typ domain.UserType) (SearchQuery, error) { return NewNumberQuery(UserTypeCol, typ, NumberEquals) } From 60ecaefd72063944ed8fef8707ff13542a1ec5e3 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 21 Jul 2023 14:37:07 +0200 Subject: [PATCH 28/76] chore: remove on push and merge_group from pipeline execution (#6254) (cherry picked from commit 01dce17b142ffe86383cbba58f5d15e1fd0da476) --- .github/workflows/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2646f5258..192beb3d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,13 +1,7 @@ name: ZITADEL CI/CD on: - push: - tags-ignore: - - "*" - branches: - - "**" pull_request: - merge_group: workflow_dispatch: permissions: From fa7f13964550907346c527943a42283a99794d89 Mon Sep 17 00:00:00 2001 From: Dakshitha Ratnayake Date: Fri, 21 Jul 2023 18:40:16 +0530 Subject: [PATCH 29/76] docs: add guide on retrieving user roles (#6248) * Added documenation for retrieving user roles. * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi * Update retrieve-user-roles.md * Update retrieve-user-roles.md * Update docs/docs/guides/integrate/retrieve-user-roles.md Co-authored-by: Fabi --------- Co-authored-by: Fabi Co-authored-by: Livio Spring (cherry picked from commit cedf4dda5b2ed7d8e5ebfbeeed4fd2cc7aa277a1) --- .../guides/integrate/retrieve-user-roles.md | 474 ++++++++++++++++++ docs/sidebars.js | 8 + .../integrate/retrieve-user-roles-1.png | Bin 0 -> 151260 bytes .../integrate/retrieve-user-roles-2.png | Bin 0 -> 206633 bytes 4 files changed, 482 insertions(+) create mode 100644 docs/docs/guides/integrate/retrieve-user-roles.md create mode 100644 docs/static/img/guides/integrate/retrieve-user-roles-1.png create mode 100644 docs/static/img/guides/integrate/retrieve-user-roles-2.png diff --git a/docs/docs/guides/integrate/retrieve-user-roles.md b/docs/docs/guides/integrate/retrieve-user-roles.md new file mode 100644 index 000000000..64f5d9ebb --- /dev/null +++ b/docs/docs/guides/integrate/retrieve-user-roles.md @@ -0,0 +1,474 @@ +--- +title: Retrieve user roles +--- + +This guide explains all the possible ways of retrieving user roles across different organizations and projects using ZITADEL's APIs. + +## What are roles/authorizations/grants in ZITADEL? +User roles, user grants, or authorizations refer to the roles that are assigned to a user. These terms are used interchangeably to mean the roles assigned to the user, e.g., the ZITADEL Console refers to the pairing of roles and users as authorizations, whereas the APIs refer to them as grants. This guide will use the term roles for application-specific roles (e.g., `admin`, `accountant`, `employee`, `hr`, etc.) and ZITADEL-specific manager roles (e.g., `IAM_OWNER`, `ORG_OWNER`, `PROJECT_OWNER`, etc.). + +Roles are critical to managing permissions in a single-tenant or multi-tenant application. It can, however, be tricky to retrieve them, especially when spanning multiple organizations and projects. + +## Assign roles and memberships + +Human or service users can be assigned roles. You can do this via the ZITADEL Console or the ZITADEL APIs. As mentioned earlier, there are two types of roles in ZITADEL. You can have your own application-specific roles, and alternatively, ZITADEL also has manager roles, such as `ORG_OWNER` and `IAM_OWNER`. + +Follow the links below to assign roles to your users. + +- [Add application roles via the ZITADEL Console](/docs/guides/manage/console/roles) +- [Add manager roles via the ZITADEL Console](/docs/guides/manage/console/managers) +- [Add application roles via the ZITADEL Management API](/docs/category/apis/resources/mgmt/project-roles) +- [Add manager roles to users via the ZITADEL Management API](/category/apis/resources/mgmt/members) + +## Retrieve roles + +Roles can be requested via our auth and management APIs, from userinfo endpoint or ID token. Currently, manager roles cannot be directly included in the token. You will need to use the ZITADEL APIs to retrieve them. + +### Generate a token + +You must first of all generate a token for the user. If it’s a human user, he would be using a front-end application and logging in via the browser or device. An access token will be returned after they log in successfully. A machine user will use a script or other program to generate a token using the JWT profile or client credentials grant types. + +How to generate a token: + +- [Generate tokens for human users](/docs/guides/integrate/login-users) +- [Generate tokens for service users](/docs/guides/integrate/serviceusers) + +In order to access role information via the token you must include the right audience and the necessary role claims in the scope and/or select the required role settings in the ZITADEL console before requesting the token. + +### Determine the audience + +An important concept in OpenID Connect (OIDC) is the 'audience' (`aud`) claim, which is part of the token payload. The `aud` claim identifies who or what this token is intended for. If the recipient (e.g., a resource server) does not identify itself with a value in the `aud` claim when this claim is present, then the token must be rejected. + +The audience is essential in multi-tier systems, where you may authenticate with one client (in one project) but access resources from another client (in a different project) or when you are accessing ZITADEL’s management APIs. Without the correct audience in your token, you will run into errors, such as the ‘Invalid audience’ error in ZITADEL. + +You can determine the audience in two ways: + +**1. Use the explicit scope for ZITADEL to access only ZITADEL APIs:** + +If your application needs to access ZITADEL's APIs (for example, to pull a list of all users), follow this steps: + +- Add the scope `urn:zitadel:iam:org:project:id:zitadel:aud` to the authentication request when authenticating the user. Now, the application can make calls to ZITADEL's API without running into an ‘Invalid audience’ error. + +**2. Include the project id of the ZITADEL project in the scope for accessing the ZITADEL APIs or anything else:** + +Let's assume you have a frontend application and a backend application under different projects. Here's how to add the correct audience: + +- Authenticate the end-users to an application in your front-end project. +- In the authentication request sent from the front-end application, add the scope `urn:zitadel:iam:org:project:id:{projectId}:aud`, replacing `{projectId}` with the project ID of your backend application. +- Now, the front end can send requests to the backend. The backend will validate the token with ZITADEL's introspection endpoint and will not return an ‘Invalid audience’ error. + +And you can also use the same to access the ZITADEL APIs. + +### Role settings in the ZITADEL Console + +If you need user roles returned from the userinfo endpoint, you must select the **’Assert Roles on Authentication’** checkbox in your project under general settings. + +Assert Roles on Authentication + +If you need them included in your ID Token, select **’User Roles Inside ID Token’** in application settings. This has to be set in your applications as this is dependent on your application type. Navigate to your application and select this setting. + +Assert Roles on Authentication + + +Alternatively, you can include the claims `urn:iam:org:project:roles` or/and `urn:zitadel:iam:org:projects:roles` in your scope to achieve the same as above. + +### Retrieve roles from the userinfo endpoint + +The user info endpoint is **ZITADEL_DOMAIN/oidc/v1/userinfo**. + +This endpoint will return information about the authenticated user. +Send the access token of the user as `Bearer Token` in the `Authorization` header: + +**cURL Request:** +```bash +curl --request GET \ + --url $ZITADEL_DOMAIN/oidc/v1/userinfo + --header 'Authorization: Bearer ' +``` + +If the access token is valid, the information about the user (depending on the granted scopes) is returned. Check the [Claims page](/docs/apis/openidoauth/claims) for more details. + +**Sample responses:** + +**1. Scope used:** `openid email profile urn:zitadel:iam:org:project:id:zitadel:aud` + +**Sample response**: + +```bash +{ + "email": "david.wallace@dundermifflin.com", + "email_verified": true, + "family_name": "Wallace", + "gender": "male", + "given_name": "David", + "locale": "en", + "name": "David Wallace", + "nickname": "David", + "preferred_username": "david.wallace", + "sub": "223427827918176513", + "updated_at": 1689669364, + "urn:zitadel:iam:org:project:223281986649719041:roles": { + "cfo": { + "223281939119866113": "corporate.user-authorizations-io8epz.zitadel.cloud" + }, + "corporate member": { + "223279178798072065": "org-a.user-authorizations-io8epz.zitadel.cloud", + "223279223391912193": "org-b.user-authorizations-io8epz.zitadel.cloud" + } + }, + "urn:zitadel:iam:org:project:roles": { + "cfo": { + "223281939119866113": "corporate.user-authorizations-io8epz.zitadel.cloud" + }, + "corporate member": { + "223279178798072065": "org-a.user-authorizations-io8epz.zitadel.cloud", + "223279223391912193": "org-b.user-authorizations-io8epz.zitadel.cloud" + } + } +} +``` + +This request can be tested out in the following way: +1. Select the **‘Assert Roles on Authentication’** checkbox. +2. Do not include the roles claims in the scope. +3. When you run the command, you will see that the roles were returned. +4. If you unselect the **‘Assert Roles on Authentication’** checkbox, you will not see the roles. + + +**2. Scope used:** `openid email profile urn:zitadel:iam:org:project:id:{projectId}:aud urn:iam:org:project:roles urn:zitadel:iam:org:projects:roles` + +:::note +In order to stay up-to-date with the latest ZITADEL standards, we recommend that you use the roles from the identifier `urn:zitadel:iam:org:project:{projectId}:roles` rather than `urn:zitadel:iam:org:project:roles`. While both identifiers are maintained for backwards compatibility, the format which includes the specific ID represents our more recent model. +::: + +**Sample response:** + +```bash +{ + "email": "david.wallace@dundermifflin.com", + "email_verified": true, + "family_name": "Wallace", + "gender": "male", + "given_name": "David", + "locale": "en", + "name": "David Wallace", + "nickname": "David", + "preferred_username": "david.wallace", + "sub": "223427827918176513", + "updated_at": 1689669364, + "urn:zitadel:iam:org:project:223281986649719041:roles": { + "cfo": { + "223281939119866113": "corporate.user-authorizations-io8epz.zitadel.cloud" + }, + "corporate member": { + "223279178798072065": "org-a.user-authorizations-io8epz.zitadel.cloud", + "223279223391912193": "org-b.user-authorizations-io8epz.zitadel.cloud" + } + }, + "urn:zitadel:iam:org:project:roles": { + "cfo": { + "223281939119866113": "corporate.user-authorizations-io8epz.zitadel.cloud" + }, + "corporate member": { + "223279178798072065": "org-a.user-authorizations-io8epz.zitadel.cloud", + "223279223391912193": "org-b.user-authorizations-io8epz.zitadel.cloud" + } + } +} +``` + +This request can be tested out in the following way: +1. Do not select the **‘Assert Roles on Authentication’** checkbox +2. Include the role claims in the scope as given. +3. When you run the command, you will see the roles in the response. +4. If you remove the role claims in the scope and run the command, you will not receive the roles. + +### Retrieve roles using the auth API + +Now we will use the auth API to retrieve roles from a logged in user using the user’s token +The base URL is: **https://$ZITADEL_DOMAIN/auth/v1** + +Let’s start with a user who has multiple roles in different organizations in a multi-tenanted set up. You can use the logged in user’s token or the machine user’s token to retrieve the authorizations using the [APIs listed under user authorizations/grants in the auth API](/docs/category/apis/resources/auth/user-authorizations-grants). + +**Scope used:** `openid urn:zitadel:iam:org:project:id:zitadel:aud` + + +#### **1. [List my project roles](/docs/apis/resources/auth/auth-service-list-my-project-permissions)** + +Returns a list of roles for the authenticated user and for the requesting project (based on the token). + +**URL: https://$ZITADEL_DOMAIN/auth/v1/permissions/me/_search** + +**cURL request:** +```bash +curl -L -X POST 'https://$ZITADEL_DOMAIN/auth/v1/permissions/me/_search' \ +-H 'Accept: application/json' \ +-H 'Authorization: Bearer ' +``` + +**Sample response:** + +```bash +{ + "result": [ + "cfo" + ] +} +``` + +#### **2.[List my ZITADEL permissions](/docs/apis/resources/auth/auth-service-list-my-zitadel-permissions)​** + +Returns a list of permissions the authenticated user has in ZITADEL based on the manager roles the user has. (e.g: `ORG_OWNER` = `org.read`, `org.write`, ...). + +This request can be used if you are building a management UI. For instance, if the UI is managing users, you can show the management functionality based on the permissions the user has. Here’s an example: if the user has `user.read` and `user.write` permission you can show the edit buttons, if the user only has `user.read` permission, you can hide the edit buttons. + +**URL: https://ZITADEL_DOMAIN/auth/v1/permissions/zitadel/me/_search** + +**cURL Request:** + +```bash +curl -L -X POST 'https://$ZITADEL_DOMAIN/auth/v1/permissions/zitadel/me/_search' \ +-H 'Accept: application/json' \ +-H 'Authorization: Bearer ' +``` + +**Sample result:** + +```bash +{ + "result": [ + "org.read", + "user.read", + "user.global.read", + "user.write", + "user.delete", + "user.grant.read", + "user.grant.write", + "user.grant.delete", + "user.membership.read", + "policy.read", + "project.read", + "project.role.read", + "org.member.read", + "org.idp.read", + "org.action.read", + "org.flow.read", + "project.member.read", + "project.app.read", + "project.grant.read", + "project.grant.member.read", + "project.grant.user.grant.read", + "project.read:self", + "project.create" + ] +} +``` + +#### **[3. List my authorizations/grants​](/docs/apis/resources/auth/auth-service-list-my-user-grants)** + +Returns a list of user grants the authenticated user has. User grants consist of an organization, a project and roles. + +**URL: https://$ZITADEL_DOMAIN/auth/v1/usergrants/me/_search** + +**cURL request:** + +```bash +curl -L -X POST 'https://$ZITADEL_DOMAIN/auth/v1/usergrants/me/_search' \ +-H 'Content-Type: application/json' \ +-H 'Accept: application/json' \ +-H 'Authorization: Bearer ' \ +--data-raw '{ + "query": { + "offset": "0", + "limit": 100, + "asc": true + } +}' +``` + +**Sample result:** +```bash +{ + "details": { + "totalResult": "3", + "processedSequence": "339", + "viewTimestamp": "2023-07-19T09:20:34.371331Z" + }, + "result": [ + { + "orgId": "223279178798072065", + "projectId": "223281986649719041", + "userId": "223427827918176513", + "roles": [ + "corporate member" + ], + "orgName": "Org A", + "grantId": "223428842084106497", + "details": { + "sequence": "296", + "creationDate": "2023-07-18T08:46:07.692435Z", + "changeDate": "2023-07-18T08:46:07.692435Z", + "resourceOwner": "223279178798072065" + }, + "orgDomain": "org-a.user-authorizations-io8epz.zitadel.cloud", + "projectName": "HR", + "projectGrantId": "223282340514758913", + "roleKeys": [ + "corporate member" + ], + "userType": "TYPE_HUMAN" + }, + { + "orgId": "223279223391912193", + "projectId": "223281986649719041", + "userId": "223427827918176513", + "roles": [ + "corporate member" + ], + "orgName": "Org B", + "grantId": "223428980244480257", + "details": { + "sequence": "298", + "creationDate": "2023-07-18T08:47:30.015324Z", + "changeDate": "2023-07-18T08:47:30.015324Z", + "resourceOwner": "223279223391912193" + }, + "orgDomain": "org-b.user-authorizations-io8epz.zitadel.cloud", + "projectName": "HR", + "projectGrantId": "223282930787549441", + "roleKeys": [ + "corporate member" + ], + "userType": "TYPE_HUMAN" + }, + { + "orgId": "223281939119866113", + "projectId": "223281986649719041", + "userId": "223427827918176513", + "roles": [ + "cfo" + ], + "orgName": "Corporate", + "grantId": "223428420858544385", + "details": { + "sequence": "293", + "creationDate": "2023-07-18T08:41:56.649257Z", + "changeDate": "2023-07-18T08:44:33.094117Z", + "resourceOwner": "223281939119866113" + }, + "orgDomain": "corporate.user-authorizations-io8epz.zitadel.cloud", + "projectName": "HR", + "roleKeys": [ + "cfo" + ], + "userType": "TYPE_HUMAN" + } + ] +} +``` + +### Retrieve roles using the management API +Now we will use the management API to retrieve user roles under an admin user. + +The base URL is: **https://$ZITADEL_DOMAIN/management/v1** + +In [APIs listed under user grants in the management API](/docs/category/apis/resources/mgmt/user-grants), you will see that you can use the management API to retrieve and modify user grants. The two API paths that we are interested in to fetch user roles are given below. + +**Scope used:** `openid urn:zitadel:iam:org:project:id:zitadel:aud` + +#### **1. [Search user grants](/docs/apis/resources/mgmt/management-service-list-user-grants)​** + +Returns a list of user roles that match the search queries. A user with manager permissions will call this API and will also have to reside in the same organization as the user. + +**URL: https://$ZITADEL_DOMAIN/management/v1/users/grants/_search** + +**cURL request:** + +```bash +curl -L -X POST 'https://$ZITADEL_DOMAIN/management/v1/users/grants/_search' \ +-H 'Content-Type: application/json' \ +-H 'Accept: application/json' \ +-H 'Authorization: Bearer ' \ +--data '{ + "query": { + "offset": "0", + "limit": 100, + "asc": true + }, + "queries": [ + { + "user_id_query": { + "user_id": "223427827918176513" + } + } + ] +}' +``` + +**Sample result:** + +```bash +{ + "details": { + "totalResult": "1", + "processedSequence": "342", + "viewTimestamp": "2023-07-19T11:24:58.769023Z" + }, + "result": [ + { + "id": "223428420858544385", + "details": { + "sequence": "293", + "creationDate": "2023-07-18T08:41:56.649257Z", + "changeDate": "2023-07-18T08:44:33.094117Z", + "resourceOwner": "223281939119866113" + }, + "roleKeys": [ + "cfo" + ], + "state": "USER_GRANT_STATE_ACTIVE", + "userId": "223427827918176513", + "userName": "david.wallace", + "firstName": "David", + "lastName": "Wallace", + "email": "david.wallace@dundermifflin.com", + "displayName": "David Wallace", + "orgId": "223281939119866113", + "orgName": "Corporate", + "orgDomain": "corporate.user-authorizations-io8epz.zitadel.cloud", + "projectId": "223281986649719041", + "projectName": "HR", + "preferredLoginName": "david.wallace", + "userType": "TYPE_HUMAN" + } + ] +} +``` + +#### **2. [User grant by ID](/docs/apis/resources/mgmt/management-service-get-user-grant-by-id)​** + +Returns a user grant per ID. A user grant is a role a user has for a specific project and organization. + +**URL: https://$ZITADEL_DOMAIN//management/v1/users/:userId/grants/:grantId** + +**cURL request:** + +```bash +curl -L -X GET 'https://$ZITADEL_DOMAIN/management/v1/users/:userId/grants/:grantId' \ +-H 'Accept: application/json' \ +-H 'Authorization: Bearer ' +``` + + +## Summary + +The process of retrieving a user's roles involves understanding the audience scope, getting a token, and accessing the correct API endpoints based on your requirement. Following these steps will help efficiently manage roles in single and multi-tenant applications. + diff --git a/docs/sidebars.js b/docs/sidebars.js index a90e7a0ad..6c66b5f8e 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -183,6 +183,14 @@ module.exports = { "guides/integrate/pat", ], }, + { + type: "category", + label: "Role management", + collapsed: true, + items: [ + "guides/integrate/retrieve-user-roles" + ], + }, { type: "category", label: "Build your own login UI", diff --git a/docs/static/img/guides/integrate/retrieve-user-roles-1.png b/docs/static/img/guides/integrate/retrieve-user-roles-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f8bda5860bcb1ea77667166a252502dcb72002d4 GIT binary patch literal 151260 zcmeEuXIN9))-H;OWh)kn(xM_FAOxh>s3?dCNRi$I0jUXu7OIGdfPkog(vc=zdJ93M zL_tD{^ePA;1c)I(fIxCr_Bs1}=R0`rzx&)jo9BsHCTqLqe7)U@lf$C%!{cL?9C}OAN24xt za<)%$9Tn+pF!?A7)xS0rG#5Y)g5#R$sarko;EpRz%#TM8$KHMuQONufe1#nyexMhf zm(#DQyYKkD%%fjy4n&xrv+ZcR$z<|uv^mLlXx|05GWpxpv=O>>^uajmPb^H4q4HtI zpSmM9PcZp$7+(}W@c8mvcRqVm&KaXj!Dst!Aa6%i{;azbCH2!Unze${AAX78C>9x+>6OQ)KA3c$*Ey18%kiB0^sHXr z36~o?*FU`;QG20ZA($2E8Swq3`KKoe%!((JNb;dSPVn5|z49$O>fW!j@1EGmw!y+P z4OqgL4m@8!XVnHi6lFZ@%BdS=uz7}*Up`wiKL&Gu5xUbRc^V4tLB-d zroH?_lEpUq!gYQ2w!#xioIg7vL|r@B9V_2Ib8UMi@3g~t0!<1(zt1zM__6^nZ~jMc zH~364xJJSJQ>^|pbNI;NtkoSGH;dRS!SY;7r>wu+^3gcZnTr`p8!y0j zUl=b0p5Qpx_7y$Du4nb)Gr!y{F8nq^>bd37s2L7BQqGOKb938%c^@P$UFR3FN^LzH z9ghwJvz~m+vVR}nZJTbU*Vm4p@Y;}BYf(bzN=!3Je%T*5ooVw-{bHB@(_h?dQ7suK zNY`6_)iIfZLG0IQ1PRe*#m9F|q4BOa#r^hss2*ZP{Ia^hBgngvcz|C`{TCaN*Q8!z zQrRSTm!1x?ZI-(G<>uuxUtel8jJ_0Wu(WIwd5fQ-!J;tPEW-CITaE$DASh~l%YTMox4 zJ4+esY8!};$LJ%c%%AeLjh4S2R#>_`$h2T`bnv;i4LZlq&s!z2r1U)0qr%TKD|Q-0 zdzM}-{hCFmTBk|J`e#u1PC>t~w2k&D?bAmqDvsjsZr+;4j@&4nz1>jxy7DMfKQVe| zef^Tr#>6!*MHHM&XZnm(zlZvn=ipOM zZ=UnGedJZ_!}B+8KKXWSUcmm#ot*t6Prjd{#$Wn*(epU|hWmN8msUSzRu8w|<1job zbEBkJ?=rLf`yY??EAn^W5Xx>}*zYCWaJ)p_H+y|Sb8Wx>v1N7ZOy)D6-bnI&V~2k_ zbLMDthkp6pyDz#w-zoPj<}PYY{sBil@wN*Z)<}Jhg(4o6KlX)lZ9GZK&{IvBc76x8FF?llbCf|FRKIWBn zcH83~iXMkOm_5Lx1Le`1&*iUOh+os(xCqjhj#Lx5nDBD&wa;s_+ew-#Ic#@2?_9cr zxzlo|;LeR)#GSc2jc;N_EsgvO*WMND)LaaR&;F{sD!qDjRn}8#>q33R&b#;5%kwtx zow}^>rv8oh8^uniPRmXndwY8+d)+w``_NV1Iqy#Dn;sF!J=%x&1-AL0^)(IRKU}## zlI3}0F0qqGB*FRCdVZe%(E_aknePVQso&WOrSEIpuMyGpu5r$FZd$Y-NOFECqa~9! zdZ602+Os;$nQLJC``1OI#Vg*i+gH}_tY2E^S!Cvxj_Kp>*#roxG_E+`) z(f`v)ym!11aff{JSoS-tiL|(zsvFO;bcsgv2m6xt!OlJr>}d8t!9ZC_!E#|*j>`Q; zed=4E+T(e4xvhg=v8H+OYq$oCkfnfSp18GdxqI4*{RG8dU~Ol8(?8cgV7(L{)Spja z#p4#sdZE3|j*oeU&$e;jl^lKq8_k419Xv!lIes!%;P{I=x98lWXFJa7$c;M>KajV1 z=*n(;@v0frtMfyT33_-+9_?36@|d+cog7&f`RQS`X7pEhgKU~+Pj*>$Z68(pn6{d> zV4nG1C2dHGX^LQSx73)6Pfw~XMxsaB+HKN#68gaQq?2Up`@xwS!I4)F-nrEeR!pom zB~;(97`Rb%u*h-Hf6$d+OE4oe${O-OQohO#$Qrvv*8DMMF?OsbY3ZJlT%&8_oyO>m z4^53tpMt}JhHihp-4p*gen0>!|3O}YFN)Whk6(UKUR@pocZCHiblMs&e&EO+)d+%n z!aL!k^Incs^9vvQ$m()&!OO0=Z{DR|sKp!Jhkf2H_b;q=4ooHx8~WDsc)G><-LlM! z#*BU0e9HsU0?H!7H5xP$(8r!}3Op-+>M{6w`s}pA82JNE1MFLNqm0=q1fk@I^Czwb z%Xawx5%R4@GA{62puDPiU_%fdg-4|XHSXTsl@A^WX4~!FMbO4}Gp@S5Q}{OVEoq<5 zzM03Z`-rAi#qWwwhnm>jwsEp?eCcq{_TH`DU7O13;_1<8+KJki`13+H4rl$y61@Ta z7&C0)bF2~aO~BQ`#Jui~U!dQu&DKMWhdge=FLIydtQT2ze`EhfqVPQT&B|=!TT{{H zvF~`{7ZcC$oHjp8z3A>W{-SQf&uph{(~?9ywf|J2;J9X$=9`N(mkI=P1qUuR=^npp z^=@3n=lk$GJ8;gKoJ0ED2ATz$1!D%R9V{{CQl&!OVkH-sxBUnt19V1;jl_BA=kiGB z(+a2A@=o`^#`@-l{J04(RA4^4^rG-Zuy}N1BJ~Nb$Gn>`NBw2In&Ghhu=HDC;@sHF+H)9N#iC9O)%_*z<3)yoNZD&fh$CGE+HKZ7ZC+hE1ov~ z+$qZrv7`{D`@iL;b&V&vS?E}ZTLcG%PerAP${2B$1y+f8-rsfJUhE%$4&AkYmyTBl zV$C|uzK*VVpCoV(nHO85Xj->?+N(PscQ)OBc0amk5<2ZxhwKZno4Ay%ZJ(m*#_5Wx z7ksD{RDIuj%V=iseTi<3pqmRlhGgFD6SizW*1U3JWnuDcZDmPEcAi7hk5c>UM_yz*>dD+}f5I-fgVM2IhS7nUF3xO*!u zIwnaOEaMx4R}3B@u6`8_HO5%jTQ|YP7R`p%+X!;doQVb0*T$_SqsbEE+GzI>loU?< zhdW+nJz$P!Z~UL zgav?AKole{e4!nOdIgc875h7pS9`5#A>M0o%S41=u*|0aSDsxtrO5`A8{hM@b#!t1 z1dfsmT7xX@eAyhP32$MsJqD{|gU$0wUxQd0Z;nwXiK zk1$F5sb;@;ok4%d5}#pee`rVO3PhkYN{Pw)7hR=>jW#jPWHTk}xASD%*@>y`-oD$v z4t$92g7GO9eZ?g7-*}39+^uIVwJP>)=<=h;oc`7k$8i**O z>`gQsw6&Q;fa^m{2ljC@u>e>5fS2k%o_}4d@4L*z{O9NUnV2G+m=65&9v$GF@%IvV zF`oIyJM*h&Osv5FP602!%>94e&4S5f{_FaHC-5E9bptg`P2k3>G^KsyRDi8 z6RgwL%AqGN)~R_=RugZ8&V*lRFR5#6Y-GQ@(k>{^?pr&vB7Xknxr+yG_z};}ANQ4O zOo>^VXiRZb%~(k#N%bhK#Sv8$un#H_VPoxqK{6~R*Rgrn)8q6v*>-KNzWZZH9xBb_h|9QuU6DLxQZ7!es zeVzMS>Q#T&5k_euK(QQWqW(*ZuE9##{#!qf>}x@=JiD;RJpX7T&`sw5Qt02^{QoqX zncXZnJ|&+Qhc7Liod~#l!m`mj4wKe+6Rne0b@BJ(_&xK}bL-&9AFUy=8P9glK#udB zQVTxAE1>@3LrXHvDp&S&s_*isn7{T_r?n-Suf%HO3%{Ldp|U~}n~kBQiBJ1TSlw3V ziWRpD@)@qH!dh8d4_T&rAiKP+&vw}%bY*3#{e@jc5}~Y_6V37a6_~lsqUg17!ih6} zty$bsQKQz~GFxj%{8l@PXBe_gA(*=k-O0)eBTIJ%t`_8d&3hQBFRtpf5JzfdQ*#bJ z6H+2w>i^qWojG_w|GDaep2P1oC@UxVE~I?Uof@(p{Jua=K89(eESxmZQ3;X_+4}XP zxh*qvsvVqL5Bo1}qIZKuF#%<%@bzNTdjp(K{QPwyb-*!Kp*Lc%EqdS~EL}ZWS7qXb z=_A!8bjD005hcf=rR~L)@Y@yqsqXY`gZi~(zxS?3lGxKj*0+RYBv+A_!pKgJZxvF~ z=koiN=b5zxy2XCKyk6*(o5*g8El-{?kyK_d2q-fY3-h?ne=M{-R4cv zAAav%%#6;(C}+4oGzE6(^$%ukedPxEn>AC9 zH|LV9%~)F1{!0M^qnS8CwEpSJHBm_c@i6j)fn+}{GneSvm;i+yr+k~=`CqJ_O z%Q!Ub=Tr20E}XZNc7G$TyvP04gqgCyW`$5XgmKAMD`fet!!k3!21~Df3>4D%9{1as z@OL45=l0(=`P(M@5B$A$_PVCOZSw!yCRMm;q(Odc`NC&1TAyC~=n8TgIl|Mtw#q~L zDFtpLgOS{=5p2*3xzgH=!oEgIj&?e>x21M+>l}aZ8h)nNfv1e`v`%mKFhBNliSIbb zI(R^i)7mSvKcHJKuU5E}x)WVCyP*WSTw{=BURzo|=Rn}3@`tQ$`7OuzMlElLm&jU! z8+?{e=|ZUb$hA(MbvC(rmDxYA9nPZQlr$-uXR*Io|X?e?`x zkg2__v$d8c6tz0cj*+MK%g?=to_R&qiQ-7Ic6N17uvBs#)|DLF=^CilJ(8K>zwpd< zDq(2y6f3`vXSt=RiETN{Y)4VgS0BNib-A%>_RMAz#GTi3CG`4WUXNcZBMNHIXlLN! zn?sj+%9QJ{J}Dh5O3<^XrTbBHaDzv*&st?@6e}Ny81%~n;hRnH=!0@M)&DLQbhZHY zYPov>e_(nPc0b(S_XwWY0{J5n7$it~;C6Vt4p*v%w4A)IpPJSW%1l==N7%t2$SZp_ zrH+g8y4l~$8h%#2Mdaam8th+FOY!i4CEs6%W74Hcx3#m`VE-4hLR+>9MRx_o^?1XY$O6k<#m3I%Do;bXZNA z`WIcL?l<%GgAvS=aH}kCNn^gZXqm|A)<+HC(;^tB=X3rUo)FDTH`g?_uCwxXP zY>Wh@92CO_1aGa&nLdJiZ9Cm=XrSFU{b)_AtnU1ArR}QtMHz8w_iee_u)W~@8qdiS zskmbUlTXM)8O=ie>x=K2ER*x>8ZD2<2CRfs>C~nMu6~^v@BgqSm}DZ4qPoMrH4b-r z$0o@EJvO!uQ8F?QC|b?(s6pYA;gt$q*kf2PAt}rjuDpTr@U;#v7y@_nh*OoD;s{s{ zxm&JM`1!5QfIebMFJ?(RLrJk^>&DS ztLPP1oQ5s;qGcpn%k_`1tS5PlKo_URWcPx*(3%V9XSlwMUg6 zCI90)nXyjsd1)dC6IJhQwZ6<+XR{xBLhef`%su5nwPcG`A|mQMhi=dH9mZJu&D6Yo zzeeg=UHzJG-F7gA$O(*^J;FGfz}}D_znEm{Z8uJt{zC4KBLrg~-QxXCIB{EbMm72lje+rL~jx}Q+QHA$SF*D zt8B9_{i@A$rio4Dz~ZFqm6{Ik=Cjd-b#Hz%fJH|RVc@h4_SCYf&;1@Gh31eQ(qlE< zsG`zF?-xqR)5xtBM6$y?ij_a^`@y+60&3Sb(tZ$=cUzcPWFaRcPaLm@Bk7}R@$>Fr zaEg1AvGVG$)LeJD70@@SK@Lym|KzrqLOD+kBdFaK)Vy~hSB?RG2%ZE~uPc8&&X15w zjpo!PCzY*Hx_MHu4e|w%Po@Lco{-<+tS+SotI#7kiB0tKVgG#QsO8QZ@F(Qz zK@(w&^7^lt3NGtlyBxM;PFD0siBf9!8KYMFy^)64<(D_e%o~&E18yWwn)*#j)J!Y; zI_b6`{Bzk;^UqZwcXr4<9^LD;Xi_fy%t1C<7P8UQrDbb@1&43V!`@Y~SKhM8xGm7$ z)pb20gV)#su7#*H0Zh^S6ZzVF_JE{Jb|)j$Ed<7A&up1+Oh61+OKR~w8Pkv%4%5hFxD!Jyw=G!5#;?% zmTKj-ic(Q%LsLi-8# zz^Smw`CVD&<=V~bOHta(Y2A|veRhLPY^J-3;36+2f7DTVife`g%O!e)J2fdqQQNYD zu{BN^nX2YxxG%1**rv^2v-TVbxd~tav5kbI2`vakBDSYl1mp}0>RP6BI5}GNPnUQ2 z)ZnHAXCj57aQsbz{%Z?XNLm3Mby){O=yz>YXxRdGofA@bA=)0hu4FJ5(1E-}uJk&w zytnUt!}0@3_EVd))`RZ9Oy)4%NOK( zEJp)iRwFz3)`SR9m3fXrooK!XVinJ28`kYxqAVk7d=OWLqWKipY#0foN!2Kg!eQyI zi59j$>S$3?M~Tv0+Ys4F;dJVhXm|4LPAm^NiUpU^Xfp{TPs~K-(~Vu_3^%QXg)%)oW8 zd>#j2Rr`LeVk1?HViw#grp73JP-5j*HbK>3{UB}%i<1M#R&8H~_xq5He(+!ub$WT%C?7u?6a$YVG3KR}!YhTCC+Qv-$k6inT(Op|y2F^$OC>|rC!()NLm%u+ zQb7qfw#+rorh+A=EJ#x+;^L*$VrfeZU5pZE+o%xU1y+W8x$_{Un#QPY`4GuKNR%f`zKz z2ch>;NF61g-aeh5<37~Wb*j091y_&WMjL+@TRpJ$AF&wA+{j{IH(o`d|TVV z+v7K`VEo7n?MGInk-v~vPM(+V5d${e^!cvI?F<5%07dceK+6NxKoScS&a%s54Zi|C zy1a2*0fD?#na}_7rMBq0x3kDP(}IufdGzPmY-2^9#zsJUbcJ%_WUi2Lf>8whN2p~T z)($&eRpTX>Vu`}(KnqGr$KnIX9vt$ij!WJqgU=SXEGz21vrB=r0SoEu99t+StGeE! z%gPYy6VaKc1{j|9gk{3VX=P~7#wmcLYpo(}?hbK*c6b3;U=h6D?J@op*Y@!Ngrmuq zS|S&_OBodh2)Bg4^19`&jmF z_JxPuw2~!{b$2Bc|Ivd+_bMv@fyZR8)f2L*WBPgO^M^g@rxU4&avcC>Oxius-!uzJ zewn9k2>1fL1CRu`yfR|=1K9p!fk+{@z#z&_&JymP^H@$SCMc&$zW!|ZzjnEA|ArE8 zXvN9v_7J}p*eO#M#3b91uMVPDqN`rA(f8c;~cj8x^}+E*;+Gm=VI!xD0=Y zAIg{OY~-VSvf2{bCi)=nLN9smkd)=`*! zzc5r55{66gjV%f`KxN46Sp!U=HJ2DzyjbNRZVvNyzBS#h#?&^kUs~J=*f8krwY#i| zZ05D6LksegFEC=Nx9+0_S_Z3sfP>h5Yy!9LPx(=r;yMW?G%}A`(D~bxZl*I33#6LCtg2OU*MDD&6ObvLRDMEEUdb2H z_-#`Q4+F@-xdkz>oyz+ATJnIbGJ*_H-whdsyuWSg-v;>)r1Q5y{x-dLmie5G@Mwitc zwShG8`mt4E9K2J2vzry$_(0y)x>944>bDl$SM4$+PkdiA6XoJw07sU%-BlT2FJH^x zj27fHzwD6g(Xjaw+cR6wWyMG}$f9-%q2>W;?@`MoX;EATds)nXfFE~&4JTNIy@hm3alM=7Y$EW5qc4gQBX}Z^XyYE8V~uDIqZXD9Hrb4wTZwCA6eA+~IhbA{!Dd1Z?iOit@c>#eP*H~~ig8Ly5v z631Bwdh^0(MiLr3qCE|O5ZIqy9q}G6Tqg!DDyh7W+#Vg9>y|AK-u{td+u)QhvZxri z`OEeD4g=d8Xe%JT$>2cmaZgXPqgm+^7w4_~+HC_YIQltT(1yD9tii1h;sw2MS*PKHW8<`IiO%)rWgB0NB)-m<%HeCRqxp(q{e{zHUe0>r_{qn*qIAF5R5SBg}@KumAb~bKYsKJ}SiOW;Kl%eXi zJq(}q6wC;(SwpdDvk~Xj2uSoZLJ2>bx5gK*(x|Q#Q>dF5RH`f*;0{DWr|-bKpcO62 zBU{=45Bs!cQfEYKTWwF{dJ7rj91?g82z-cx1n~1)oXNGwvo3BQgLa7RLs845EFktq zmhe~v;qm2b`q7SMl8j_k&wJ5!fJz*ncJW^PE(t_R&-{pCaXiYZHA=M;o?-G!pC&5C zqydhX7F9Vzr`5rHXdj-C%S%!SdwGmQK!mF*fnvp_xK$bP`u;?b$xDGP2$$;Q zy=s*X5CifL&IZ6xi+4gIk75){7q&DqU`7_OuQKf$3>gw#fu7?^O{en5NcA)-_kZ{q zwlN_*Xl9rD_Th{b3EKlfr$C#z%-x%<|KMH=@>tG!eV5nVbtBFsFrLMBW5gjA2(2%o zsKKy6ARZn;%Mj}bEi>btpJ@Ta6Iyhq^3ByER5>|4!ZAA=muffoClS^YVy4_5agLRZ zc775;Y_0qC%4N1iBfStAnB$q8=l~EAQMrj0((~!?nuXD7tw6m7if9>%LxbN}-2(B| zNpeaV{&1*?LenT8FHHGfkLFCF2@F~W47g4Kj5i-6ftWT+ub=+;7AXzg0mu}PP`baB zkL!qi;tPN%4L9*5g;yxdQP&9>XZaB0&9m4ZR53hgHwGeBe|rgqO_apGz_z!Y)_nt< zz=7RUUR#jzSasrRd#sP}N_6C}SIA3q=$u-ST4;P5LYkVi_gc|Itn84xO`W5g+dm)0 zoW{?lF(gXT#@jAs&8LM@{oMnBT-zMp6j7lm6uX5>DU#;+W@SpvP5b&|{AgJ*d?kb! ze0iJg%$x8za*vW@Dq``ZX+ZAlepW3|@DhGzCnK0YJ;n0)j;+g9stEll6{0vi+B5fz5+k z$+ePn8;Hy;d|Ua-d#<^9};mTT;S35uM#Mz=+joM7=ti|h*x*qbopFHOaBDlu1Ici29G`rs1?i9U({q8hU{!?N$HAEc4b6+W@ zD0o1B*%0}BZ;*ejh!GTTlN98wGo|-$0)%p>`X55E(x(F7CCC`{}!xUlk# zDdR%8&ggBl!#f;!gl1l<1mU#wLn-2NUP|qj*`Z^-H!{3w9PO8vS3^4=OHIhJB+TXV za_8$|61v0#_)gYNdRWhuy53yV9!lp*Q}QmXgme=p>ft~(@Rbht*t9YW$+E#qI6XR@yeWP*?~&T2O~5c)7A3v6a}hBT6w&#Ks+G zCDy{{O8uy5Y%?EDV_pDza}9=;C%Z_uc{OU=>)X70G%PYe+b};k0mNnL6rMXf1J`-9 zI0$5lQ}g4ugO^`SEc@IgQdVlFf^R~WJq@GH`_^NkmV zCs%LLDaXT1B>$i$Md{~GhoX(8(ASwa;BZk=OJqpzi}qL6F#TcyJ; zbi2-!k?(@^Cj888>{O0m^^hiA5yBrdGs0k$DuT3k3tV4!lKbDtITfa0G02>+Y)@7y zsj`A(v1l!WY1D)`>#c3$Lr(7=y-)AMbyh}&2)_)zW-=WY(zUIjh+^c~4xCB>Y8l*GpwJ{^^^H=^{csh zXCKL}ftfpQH9O;dM6A?9_hvWl0}4ikfB-gtXK(D+;I5t6`V_BzE#HOb2*x^SdD+A* zt%SSDJDGp(C9_WqBS(+n{XTbeG49%=U$n~Nfy{Mg4Ex)Rmpyz|C=Ig=__khohjWOS zPq!@H^A=OajYUy$#cFvUN>JRJNm8b5qs8s(Yp>Zh&N>=Y=YaOtjNm`{P5%(;in~16 zqvE5*0FWa-+dr~AOG(U>5lNp>ckGf$GnBRMGo3QVDjglI6)qhGiJ8~cI+Ev@<~ve6 zo(jS0%4z3Jt8~ju4zF%;S);Qw(vo-2=_7QK9d~o{S1zm5nE{g^3qkfB~!c|v|( zM%{=8lo*QQ$hC%ef4f~^LDo2llRM)c$9m~W`2=pxXScJ92qmccApmT{VSN7}&Pm#v zTV|^FmFgOTxZG@Ai&0lnKVpY{m6vG>qt#b>CSx9k_CH$dSPUEzOOoC4=+$GXNPwqW z2ac?Jw3RzP>?xt+Uheve2j#5Q){kN>aFyS7smhba*8cuin!eAB2m)##+u~Zh_&T_! z1`>pew52OYm-0>}sn#eecAaD;^7ACjXMp22p$ntZS3|NxR;+kOy(7Bqnwx5RYOrgU z@%2tQ{%Tz{=I#J@TW9XGP%SqZd4Fg-=#BrVsc6gA+l?aUY6G>JjlFbW6%yEO*!O7fqLR!~Ya-(9yCZ$dNhbbs*tZU(|(r@pXG0M-)!a z$eSLfewvLMGXxAJzQSQNRV;60x&ZL;fot=2?WMbH3X-KLT8%5Uz5fvYYk+QYf;Et` zSZazoP2lLThouJx$}et)3pog(S`nuvE!rMX85L6l)xpE0NCrF+Sf4tNf^ z%Na(+Sau<>Dk7l965z%#+{zGL@(O_!2RnF6KaQ{YHu~b+{6=319P+E zQnBKN66A5MqpvF&q0>!99g2T4hgj6y1CULS0QI;# z5ICK_4y0;2j_Qtcs;tI&4`wI@+zlhg@+oy3A9Lk(dgFbhvkP?I?L*ff{lxt6L7Ziy zPo#9v9fT(KQx4vS+uZrQysmc<=w3csbY6TPL$=l|PRVCL(RNYShIOlUR^#Y2JH6Z4 z6e4n~|E*)`NL1rez%P=ATZYnm&$VF1w|OWh%&Xv!Ti8{?ph9&CD-LgOR++YFGrBkO zJcDC|0bYS@nA2)=hP+OVo-0d|hcWpnK81C-!LAEIh846|H->@{dFs!G7R5^evnb66;>XW+1Ioo^#IQIIqY2}$Z0ZL4_tXa{5Hr_53 zKO5k;id=VJGDtp( zDlhQm_OI=mppeYtZfy_kcdDOROqtCT(20cWKtu*jmdi#^H0FctPjcBxV)6a_PsnDE z@+H@O`SxT4u3;H7Lj%U|K|RLR0qC$^N9KHz4XUa@zHo;Hf9|8UU*ai#XQ>JVr6!Kq ze6zqNetD|Yq}B_TuKNC+YTmoV4%Hgd(z}4M%aj8g?<#(6Gp#l%r z2cZi59wO4$rROj$)aBG=UC7fNr>*rFV2)qG@TA=cID|28OjAdX)$d)a*+RftZR(&* z?|`-FlBLK^>s!RF`4p&Kd@jw(HO?Job@qiSprR{pnQ!Ez6kb7Y%>?zqKOe5J6qB6U z`73nv`{`Na%F9q1mRpKnL4Dcpv76w?2|5)-+>NuXzb_{Y!VC^s20QFHeZ5t&`WFUP zM2l+FTKnO>J-(CZrEny&?>qWBmX}OK?oiR!dYU&ET@@17g^ zxNxijX{qk#>J;Rf8U0xT1?E$J;986DO}qaW*xMRMZeCt(94 zl7L)>i)wBgOK#_~Jm(GwkY>*AZm-AP*Mh@C*4O3QQ3*ySisH(II$P&gUYXTT$ZJb% zRw42!d4dUDTHMu;V(F&tkNwDBYR~?N1UGMAvhu@M*G<22$%f)JV)exS+6>#B@VgsI zKim9PLdw{w{kO?SdsJ2DGkh_&Q-Ip<6v4I5uG~_=Qfm|e3`@IDBR_ldjy^>#3vc%k z*!o=V_GW7o*VBARqdY`*%I)w@n%>s=(X}$>yyW-3NZNd(H>OM)K}e(6wGYVk#O5mu zH7`RlW3S5q?fuoMm>{ZDvuucp+nc5Y4%`3%1bu@)HD93}yT&~y7{k5Np9@d>hzu+a zHc8$9rde>O2W!VcLa7zXUU>tM{Y)IAi?I|pw?%c)>!po1Rt3Ua#ZIQ?A5kV<%TNT3 z@>&(PYUD~lJaX6`B;p=`Ma)I29ALq1Esf|wxf>%DBz!jTmjY zogmuw>Q|>b=`CRqH!1xZW{p0}`jcrT2ANIMuZqx=DK_y9p&~$_nIAg>93Va3v zV${5M{+(|FN=NR5A}BM-(gSysJ@Y&j=-=$@fQ+%2Pj3C#6#dt069COrsBR54yP16} z%h2)v5jkcXrJreKrApl?+WZl5UJ}P!*)iSkk7_p(|4eme!HMY+Bc0Z3E~5`F`QAP8#9oRk>AKIGcG-r@0uro=V2eiT2Oix1V9A?!rce@BYXr_93Zy6 zzER%Y313X&vVx_{6Q|G}3^g0>9ye+Ci(OA!X#nV~RFAaf4y7Zyl4(6zwH6)BnsG1H zT!Cx-!+Z;kA;%^hSKRqoHROJ7XW+UI4~Pt7kdUkUm}0^suN z3MadjI?{XBbg~``?pV;HD$iJQ-u#bJ2pX|{$c1|=EwPhLClB$34Klyx8nUGe2yE;^ zx8|d|<*bw#bj$@m9f57R$r(y!ePHmecXA4~>NH!VeUjW+~E6;-}go7_7Kl=QhAAiqtH}9uE$$ki}z-2KqJXb!V?$WLov10m7G(+d^ zae5Q=$FN;vdgI9q_g#9#_iDUDyFMULwLVT~kusq!JV2uWUH zC9M|O4T=kY(={m0aWd;?7l9_NB+T1FG5rMw=zca%c-tCQJ576F6_DIWBTSFbxXk@% z3zF9yejppOE+qW#;;U&I@aIQMbU<(bOkY%xsV{W&&%xu!@cArhDr{*UGR!Lt7 z?%9otL`?;np0{Pur5|8F~-=M9oDaABpDeckJIGX~? zq&9XMnb?TmG^l0hX4Z|<&qjkQK3%p}><$Ya?#oAu)lMmO!qX(&S*oyg>t1sI8PZ)V z0T>-J=gHI|vmxGQ!fVy>`l_HlSOr@>uS)4S4oJ!b$JE$RMLFo`6Tfd(MlsiBQ} zyB+ny(Aps#nchQ%|Khp-x_Gp2UyHl7xAtGZ1%QYZk%xv(&L!@}9Ka`!!T?l+>^p#S z`$#Fgx@RyMkkITJ(9`TA81qcudcoh`F`CJ+s&os2XQ`?1?jDjGXjv#HL%;%1(3}gF z?t3|U#vQVkf&HCF8#Z|`piE@1TLK0klZC+*0CYHuf0Fs{&qE#qHaN2S>D1bm$)e8R zcNj3w-zNEA6Ol1v{x-?~W0Qm$aV`v(+r2m=`98tr2Wjuzbkk-yl(#^#$J=sUu=nF9 zFRTtnBN1tTrJ@%pIaVyy&VcL2Rso{obw88+e&i1EpuSlBuU7xV-EjabnpX{G)hEh+ zxJoZ{eaKWSR{3Gv!U+6x{3LL29Rj3GXLGQaIO|}^BS9C>DecADj9;?QIWG_1e!4$Y zR`?@%Bc**`0P)`UUB+=MARZ9OOLvZ10FF|W(~HSv&rj>Blnop0akeMU-D6;h=tCHS z@3FB1qp{a^dv`*>D9*-}pof9mBU~d3WI%i>I%w&VuoSMJi5eH-K~q7&{XSOrwD;zz z{t*C-nZr;F)#}PbL;J^!;HWMjH77084$fXO5#9C1zxAF!TS5Ml|C}wMs3T%dUof(I zfFFi6$;?E6p0yp$jsM0tlN5F(P0>4wA#rEm^0&ZQe#S2xj6{e|1<3 z?AY&2!O-5FS{9RUo@*dyt3@0i8C$Lx`jH!7Yn?cT>{|zp%@_?M$sC41;Fc@bu0=I9i?aq&FZoclB1vos-h(c7=+%yt6SMkU2JwH{#6{$7>ibYD=1$nTR z>0U=KeG%9P?jOKCAwQ!rJ7G`;v5?CQI%=eVUccH=w^b9cXyLP?M{QSzcPB0rC~ov& z?=k_~FTEf_D%On0O-8;9lnmKj(()J!Ax7K94ht#H)BE2C`=NSP#uz8_;`OXO5gGEp znHTfu3HaW5_K#C|FQ+YFsZy2bfi3!@|n97KHE2VLu#m?h<>mnAsdW81S}L%B}_ zGvn9=d9KvjwvZIz4^3O6Vt;c z{Al8m(oMRB* zjB}oIx!#>!VwDdWhgJcC(J^XeqOk)|4)B$sHhjimU6$Gh^YObzW)JZFN>>zK{ zv#o)&viG8KeY|S#)}{fyYyMid;IA(gS5K|oAiPO16e)CY#)QV#;Nl{ zZIThomdU|6gxh@1!yg}b^q$Ob3m}GA;D*mriTY2wypO+;?MGiNYx)seN9&+-Msx~f zQG>By@ur<;!_czjY&BJ1Zx8(f2^&BsLVj#l;XOwazLaq#7UDVe);4M~p)q>Hn zb>yWc5tLS2>-vwso{-x@sY~Cdskv~JsAa%U4@Y+5W*HFVK)zkI^VtH9((tlDHtynC zpCs5tYt!{m_LW0`@Z#1u^^=`929)BfnMDJr_uMiJVsU$bDJOYnvZG;}p=Z zc}aYG2#`Xz87VDUyF#lbxyv8mL+0jysP;K6_fx=PLEhyU{tV@@wEe%tYNl!f@4N{k z+iDcNOn;p|)MQ@U|9pL=J!`F{yTn&Hb66)E3s$CXx?c1i-6Hq2&bG#>#(=c4Xji1P zL8rC9ODXwo{`yh=)hJ-_?D^)U{*V_cTGVB&FYktaN=>(^EekRfnSi>FjbuQb=+29aj`daSnac zf69<39rW!@E{J_`sK_XcEP1%w*pvkJSUIb43x{de?ztchZV3HNDp!gDWncYyT=4>R zD!Q!waDFNeWOoOCsqV`Sa>ozW<#ZQyUXLX_X{HPMa4gBv@6O$;9%aRW-y$8`0Vzzb zJT$o=|AqIEm5e)bZbcE)T?|UKMl6f<;qsKC3H{$C8TzI2ARu+~CY|D66SP{++QD1d z^C|&yS(^p^>4DI#WNRdHslLl7*U2IAn=C;8RrfEDLLBGVZLG-|Qc?F*wd*T-LeT?T z)3{NWV#DOC^SU0N3vlhgv`MfWx(BlgHu5K~(VR4aR9byC0W8E<8S+g54XT&`(kqfd zN$gS9zFY6wk&{jjgFJ6%Q?dO`0k!hGIcfr1`GULG?nSrM_bR*ahwOzPg;#(rpoIn3 z&4wX1;C{#xXNoiy3ym#y$`%sh=27L+5q`;PN@8;-l*21spml0t*`R=x^jpq;GW=Z?OvtpP8gg&kDq>sjhE^Rl-BW!?%GWG8!jWj3=iHwREG?PwTvm z`AkE1DA`B!f7p~(R29RI0>2b%K+bm*N~Bc*f-70#tGq}}Y;@l_XPGNU-ie>+! zRa1xb^F{7+ac9;EQ@ptCAwxU!M^?w|d-lSWM|XjycLBdN5Lnw3_X1$wkmaiPmLLGl z!*1X^?lHvd0pL##bSH>aTZ#K9MilowxIkLF>qjo$tiPgFmIqxZDwdMa71FN(0U?$~ z>hOb&cDJ2wdhO;agt{k#cAr$nc8O~Xv<*!>#C`aqjWAVNzPws>6_5b^eCyReHSY{r z+?3M7_8~`y1-&8CoHjM+|6OGaoljM?TEmUjGT(S_=IVur*+;gTRu=R;E zG=@B7s`;A|Gr-yA6LFBOK)ayKVQYn}JDo0f;89b6$Z5vzbZR~;ZxvC8gl^{&Nxv_i zb8;?DmFAiXWKq2HU`{%jDS2n=0oYb)o}iJJ;j#Ka5{_rioD*DzI%^t~FV*zKj|9%i zS$OM63T?_N@kw;*k>;p1|7i4vmiKsIN)tocm{er$XsKGZWQ?;Bbv@K=zHdTcFZxhF z1)zb844k+*e%f|*^0htG7XUJG(cs45oQ|^Q|HIyUMm5=P?V>hBKm@TMB4DA2G^K_v zDxlItdY2Y@4ZTVerK(5?geKCPNC`dC6-elW&;ydtTY%8nPyE)q)?Tk4-x=q}-ea6` z);|V3JZ0YVzUG|QEccy|Zq5khj+3D&mmTU444vA6+|`^Mkb`!$Ejyer?FqqgNqyJx zdpIL6)>n@^Qyt4PPU^7wQq@j_@sI!Aa64k?H=yKXYW2h$5^uvReusUw=Z?7VL{L<; z`}p2ho%sKHbLS=vi+H~?vmXcifrjz0;`(LuWQ|~3RRfjYtL*MtvKM0?{}B4C2okL6 zc~haHfi@r#6m&^LHG5ndWEi&}&mxD6Ft z&Qgh4QofVIC4FNU_R#%92rjaTW>~VOUZQQMm2qEEc)M*LQxB9-Nhymh1(I^_7O8G# zs;Q(F`sMnS4>I?pLy-$(Kt_xvQER zF`@t-cnXIo#L)jD*RyWk8LI8D;$WsLMe&|8V|G{mr`*)ymHy z<*l#(H_@6diHZ_=2uQupM#FDWfGh)GB-4AzrHc#uW_o=ly&AhG}!5KJamxBr~x^EbSWOLZt(+!eg%Tc_mCDW#a` z_e49*rJz>AFmZXNq_<5W^!&kt!;LoCZ+N%)e$KP;^Cag5oLz3_2nguo;gyC>EtrPS zqG0(R(3bbYdhaADoz?c4s*8TC+La3d-Z~_ejg}Zh26oq8Z%>i=gNM7%PV&nNfgZKF z0UT$@m(WExL=l8Nld;G`s{e&~E}P4~)%R>j0E!?|Ht*12GR{?Tt?2M(_n>N4AnHE* zbNCs9mNuDirpU`@sdss|q*g0QGdsjAY^yNUCAfJm;hj+znd_=4a&htTmuFM`ded7% zH9GdV(7E;KF-Lj*KKz!gy8`h1lcu}8`GvLmw(x;Bk=K3JX3&@o2`ku3;tqSn@3 z^FPBRT~e6D(`yK{v3pr z1HIuBM-`8354tu>I}E*cB0>8@eWlJT<1wuP-Yf5sRmpNTTD(9Luq}YGh zgHlWL270{BGWxtfWzlo;S$)~Iaj3mSDpk#ZBpewJLQRh9D)>SsW8`KaeUg2GT%@oR z7)B^qw)irU#SbcdR3dn<#>NSxDeZa%F4$J!oY;Un^KXLDjg)xT?szU#ieza z3(G%%Q5C%fs+tPc9W|Nz+;xD?VUtwJib%jI(H?YLhuND2 z+cHxpWreH3IG)^v@jr>AX<%q-3K!5cU;8Zs7~FD;6g&f^^6}fV(_JQxoKFgI{BvI} zwdq4Tbt#Jw?>0`NE}Mvy0hKQ^oKIf9l|AM$dOqa!6 z$Q_M6;0jekRp#^v>zKD7ngOQc3Q$jhDpELbJ3zYe;1Y@<0{>0*%9+*xfPav`VH8Mk z>bA&p4u2C!$ded)tgC+0_v@BJKDS7(K#CWxWyp6TLyod31b0Uj^QY3kAOiqCiW^k? z$bm5uU^3)S=Tj%|{t6O#vTJFl+gm20rrcALa{vI_C9cA?E)ug*p<-#+efJN>|C~Ar zXM?Cv`J)86HBiIO-PgmD*moQ$cMZLlmkV&Dah+5=JMbY0y3izvMaX1s8Fh9qDBq>T3GzL;{NH%eJb)+NLWUn$_DT_{r&z39 zVt4AU7dJ2V1yaXAy&3E!EzGSqy<}N8|MJL8AWc9VTfv_`NggL~mvZ1Pi*6}GQj{?7 z)}9hz%!@^F{YnEzbGfg5S9=UGMUdpxf~J2peSR!ut{`?(u*+lr)?ZnPf4Z2Kzt@8o zun0=+OiPmD7D@OIpTFy=8+ z4xbu8k4J?v``)VuqWwdwia%v=PMyThssXD%h?lUGxIAPI)UE+Y5Pj?Mn^piJ)F^*W z@D@&>8rK1ahE)#Wd71M({yP}ELV_`%2X}xs&u~fR6_yMidFyc~Y2p$qs{;-7JkuYm zxb%B0)Yr+{WTH#)pCwPE!~j(l$`!AFdtAn*8TQ{tgQNk;wlYiL4$iCX-iQ7GjjUGxVKzYg~LQtS&q- z@U!;CmO!!>Pfp4_IrHVt-7qFuS?+UTVW&)1>O{W21};HR9(v;K-zQ4xq0H`xrZlC(Y`+M&mW&YJ%N=Vqr^%WziVLp=WqYF3x59?a9tU= zEVkhsUa0m9#QT50!M|ThNB0n<67UG5qHVHz_K%1Cb#2<4OTcC3_ZvN_`Q}EMkAH0N z8Ct+Up+#MayH+dn?=Sw>y@225U`dxTL^v8YDTu+zkA3XTTPK4Fm+uA>g4xbG9RCg0 zAmB3fvv*+V)sLv9;~)DU(fzR^{}J84cGh1C`u_#3%j0a^(X}axyT7pZKkQnGGhkoQ z8i|WqFAL7$IlIr{W7VfB&mXsYUrbZngwe;hgcno}vmafdm$iC?zKpFE6nsneZwU04 zoJh*_aEa}E1VMcq*n8v8zb8NbL&5$+uvOsR*_Jn9do6)>e}}Yx-LO3VM5E`um5V4F z2ewJntv}K4AD3>E@I#OmbzssUeB9@p2pCo%VIc7wz8p^Hh(8`bd`nSsJJfnwRq^U-!MtCFg*`J#Bj2ang6KUzbWW{)a~!P|Nj8GMJBSs94confXfRRlQ58ZsQ=+XSJ%o%2fm8g z+u(99NarG)`9a@=Z~3paOSg^%w}5tb06}gWss%&d;lR^(I(QT0sq)Ua5Y|A=guB!8iv6-cNy*Tm1e|i?$8JyJ+0!;}PV!EC~a@Wu3z_3NNV5 zl_Cv{`wIwj(=}BFe#brVFRRC3Lm>0pSLWie4q!s)SO2 zMV_Wn;n@?iSs;s&tj^W(t1=fDKOC((N-VbUw=neBp(X&s=fE3kML~ShlH&!{?ayU4 zOjo>r=zFYPnW?_KmUYf{%u%4vlHP{$-VD1U%$9JZ2iIfsm-aCY%hf8LqJmvSPuTC$ zFzc}O=@dOFEb9%!Rb%UrfFod^CAnEU=lt`vvq9eC+8|ISdwy;LxmZEi!u54oB$f^v znfUGQ#D6o2r}|qwK{Db0Fl@c*fzDL;y`{ROs?9et#wJI5E7GM%-q2fV!B3voto!edqKmXD!+;e%f3WvcP3DLW*Sk&*&C)pY*I4>WdnFSR1>eOk`2Q(7kVfX3+)-}3k z5&^)l%dUc|R_pXo&S+^HWh-yhN~0?xSqMTflbIg()8WXB*2Iq83&mkSx%CpWBm{?T ztJlsN?>EUs@CUsaqg~d@j#S{YcTlz&<5yfCS@E;HVCQFD*>(00#Y*cZS*|A+Q3`5h z?L07*+7-D{r6yz_o%cxcv?4qus4uF-w3E=cER8YbS_$=j*e3uo{ri zd#AP`az!-rOfn|SarvzlYNeI2(1O{2JE|=FU~dHh&UmwzSOvjnGVu{nVm8Ib?9v`d zIxao3NCv#2ZI;xU{vw-Ao?}Xxz5K#TDPOI1l-+<-9J&$IGrx_?l1FdJ%~;jQDIsB^ z{^p9UcZd@v6~oRktE0t;1N(|m?4)BV^&fu4!(5VWe|sCo!Kdr*x2HN;b$E{;d!D`; z*0|%`!DBk^H+Ql0$L;XS4l>fH;cSd;&FQ|Vfh{IvYg{EaaXsGwdM8>u|0ZkwsxZFz z2#PlJuer^fL{6=#KVH!Pifpf@@sy{tOuQ6s*@=eU1<4vfi;8)1FF7@-XvyNjuzwfX z?M2rXM@SQoDLu##b((*`MTbZTRM=o}jAZaMtxj7ps$r+Z@ANNOMUPqu4?B+0{?tH^ zTc15&CpB73$>=^NJi1tK6GD+@=O)^}zpVx)aJ55b>L9=fl@Fp{Fr4=BMt^?vPtr%f z2R@ohj}D1Q5My1fMIACJsY~!>J2GEU-oFf&nrerTPZG<}5V!Kb)@ozqM_aL45$39Ee`a0d_0Kg4lJ)vi;|!GBUY zegn-%H3Z`0945PM16hBNWdXl{T_ayIsa2vGlOCN|>Auw2mg;kLIo^AExkoE$%nid^ zsc3f@rJw=tNLG0^eKfiG6P%klPd;8sfqypL#Pa7hQYURA){Cf8`drq}RVH`i9hnU- ztB3C|ee0{hy+6$b7V4;QYf5HJ0`i7Ddy)MgOM`aZgn0A>DV8g~1rvfU4E2q=C?Vdj zKD41TsT87Cy%6eXcp(ouy+KcQ0AOIJvbT`PO5LudWlHupdJ5m=HE-mwO(8RDg)k*P zGj9Y6sacQqJH!fPDYx%mZS+ zx6RL*H5G0>U@Sl=4+n;1*dx01d-EG36!*sxUS;iO(!)iP?5fjs>|$d(5yJI4rS-xq z^+zQPZpgcYmd;}Sv*`20M~$4{Vl{PpV9ryj;5McT;iRFEtMo=bPJ{y*X(&LUHV?n8 z@2K6~6=x{M>;zVTyX3?Z5l4q@FgLgO?}sac)FJ{0UIu2u+75Q5Lnh(SRR0|z+8l>+ zB4UcN1NuhK4(vsoy_P7QV70O|%xJF?ew6;K&%};mkJ*J7BTqZ z4|M3+#{5&|vYt&J1d<(T>bcI`9L-Ke@&l890;)QAEq8%z=o=?lk@brxMwT+T=+K3) zwxt6&@Jy_h;L#zWcxQv4gA=WJF*S)5fqD|A=;7S#T^UweM7`9Z#>E~>^3u6h#xrhH zM`cvE{rIOL1Yj;{5-@&zWPf$KtDfknM|4I(Iml+Pwc)%p?RmCH7H=1|j0Uz?tFdU% zz3AeaIn;gITZ}!jG7c;P<1TeBX`b7MNlHyTBGjK1zdg+Xze5)z%qEXM8a_q>Ltv90 zh?28>jkj{w%%2>MP_andO6H|HD6AX2zLl7ikbjm~wrNo^xoxVJDy$;0`DhmJkObE~ zkFHV_MhSiID_c7Rw=Kc>xpepTC;X2xJdc`GI?a8H`2{S@i}rY>poO9{UouWZZt2<9 zf6%eVn0EpQYiu#A5LjCbxgsyY~+2Im=B8f&S(U{qDkwU<}F{7SXW;ZbNm}Z>AW2?ZFTxh<((561+ z!0vt57%!9A;Wk7$(cfv*EQ2~wV%8fIf~^`qkWY|4+7}p2-I-au?{A)GKAK?jC`N|f zaFp){yc=ScQQcqi^cyDRZ{bnEy(c73hv6I^s$ecf+KQp1t+^d0I&u#@}2Ie<3?mqq2=KHjU+46PDz@?@M>gg z!}8v<+c3V;&R%e54i{3GT7(#sWoOeZ!!5`qJsP$*qp``V&=7>if6T1*E-%Yt{9u-iL;xq?;)59OQm2#s@rG7b;wN3m6^_SC~#ML9O#Mw5W{mKw&$lYEPhsJn#%0etRTD(-y)zayva#ucq@((c(<) zSp;H#H|ay#7idQKmrk9XlG*hI7+tq4F%de{NJG=RyVrDPQDPoR`d3&D|jfAzB z%X7Zs`V(AWRC7p;W|nCTyR^Bobap=BLyzk-SpNLsO|6BnE8r#D4`o1TDp%eF~DE9^w zc0ZR~f4!P&vtCdsJhJ>S;~Ot=Du$ROq-n%9gxcaW!q!#~l-LqcqJQ@cw10D1o`4H( zc#Y~JV8}#+3GRiGGv-T+##Jl@o-uZXUuR&!$*-#weHSk& zOEECp2;9onshJFApopY5l}c2A~oH*%<$a8rQWD2dk6 zCrmr;+=NJE6fwTKE~tHL0}@*vBlUe)foQ;s9Tj1_ZHU%Z6FN8>c|K#Po!7(*ZPyfZ z@)A?ON$K7VAAOJdZV8$@Z*89j`>hPL1=c!co3(?xzcQYR5jFAHHr^vY%~SuD+2GXx z{VM|rI)PB_7y+U*Nc4vqMIzc@fa8W>_W@uWu~eObZrEu*6+73mp0rp<4|J-XaCOUu z690{dPPf3ZXgBv8&qZPdyCKp-Hcg?5o z1S)cn_ejcnnXdJL>u_n-CX_y8uRWS*=-qjtlKbL~zVq`J27^0+`8y1|A97)Sb-d@v zOp*30gAhlL`rp}&}>2vQc$H6E3;5X(I@_7S&EBkPOl@#&O2NQP4%3k#7Klr2zny@G~ zI9w2e%C7J#8{I(=K`hLxFt=**TZR63~=|)4cv!jbYOU3j}*u4Ui!lnBh(y=M2 zZpAt_$bq1{Y%fL1N?(jcC>)7pI9#WrbfoNt>`-kPSkA;VMd(@-K#^t za=Q}Ly5?^z*HBZ$n&1^zpMDx?O+2?o?}ucEDS((=4<6Ddztem8va~lf)y+toUAMy8 zVkrf2HJD4Vt0`Pgc-O#B_2$FsCe@3>OhzMd@!8oC)MP`m5qmi&@@pk=U)-oZabc3; zJeKw8lN{V5k|*6R9qfh&wzt*~URh1vu3K%cTz#SM*Z#Pq(bMB%4VI6cAs&uG@WJwz zlil331UBE{N9R`2B0FDX%HN11S!l2I7AJ}B{&;*Ek>^j&GA@>3w=^9sI=Zo9pqj$= zuy_F)!D(JUqXvgi;G?B|LO(Ehi;j(?BUOZe{sz^rEC<3Foi&R%n?f>4dLMdUb~C*jg7J=qObZEJe^Tl>UcWiqmoXs_j^N@_e|a_kSfy`q zkML;EkNzPF^Urx;d*Ol4hcG6gLG%fnW0vS8JWy#ue9C_E&Zd>xkM_0!2@cOrU3i~g^z|t(kwa8QKx-NeE zLmyf(@o`@(H&Ky&&5rb;eg~L>VHSyfImpZa+)}_16HHh$EiOf4?yb@vJi2v7V!!8e zY|YX)gRlOZt{F;44OdEgseTgf_nv30*SSOyAwI+~>0}=)gm>OHCJ)YL)KL=>DExU{ z)aZ3A?V|1L(3HAJh7~D>#GX})JugEO5s~3n1_E}y$=DrWqI@F^Q>0h9BAk*UA*>&) zIfu!?)>945#ZTnjgxU0H6dJt$>AK=mBwDJ}LQj4@N5_G%v(Mx&m;#9^^?6AEqrm;4lH^4SZ+We6IY>zjW*^zV2Qzx66yWe0} zzmK^j9rS(X%yeAFtGVWwTHPE9_`|(>Msn|Xgfu|WlvqqNA!ouz7Lv}L`P<1u!Y4$O zqQrPiW!I5a%{+9wTOHA7w%!_?;lI>db2I=9kso&nu#!zS?SjFRZdC9JIenNaowY0=2w$Ttg8Y~}l;B!Wsy7lReSnsJhdgL>^k2J4k z>8CejCvP_PBL+Dt?#B~B(h`^9Pc1cQQRN=Fz41SpOf5dqR0wn=>$#Uoo@SikKU%}= z5eUlR+q+qxVE#mJ9r%TGN58eq)5zHe(?M#Pop7IdH({)V7|8WSUyK0ii#t*CN3oIA z=hD?HM!CU+dF?hz?6YfsH@i9dq@>R{AbIqX?*rGjAMP&ev#-@&{_N>k_-<^>JnS%% zmvR`l$croCB*=0fojUB4W0Kz4P{si*O~kruMo-pNEZM(2QNtv;Q3dYesVLCdc(N=Y zZrJT%9k(A`@9e%D&Ij|~UOP|fWnc^AU~%Hn3$Ax8l2zhR@@(e@E9KhS*V9K(3tI>q|j}3;@B;UpJhWKw3_|&>1b<6qY z9hLQk5m7$YuIZwS<*uoPH)=rp3@eB9mv_#6-JOy#lKQa|bZYs!6N2tmHbQ`>lgmG# z)$$re$Gtl!-kQbjm1>oi^QIFH8uX|n!SODAk}Lmn!LHuo{)sPq^c!T4Z=v82!*-vJ zZ2P?#ZKK74m=>ST;iN^wA9h(GA9%cYXt^&bGdffjVSLd{opElGC#z;RT?^D3Jzeun ztKDz>omEixB{8THc|RMg>>ng~7~boVs8ps1f%snqX6#UefH+FJFnsZ;m9me-T|r}I z;(NawAM{xIB!-OdMkqG~MjlrAG>!2)zZQLk*m?8`tjF$jq`NmMzU&mdGlw@iwW9G1 zgkD_oW$dCa=7>bFU2gd_WShlY_BMY}Nj%=FLB$<)p!^R$FlcPnb`MZ+zTW2r3UcEK z6q=n54)ne*yb}`c*w2WI|63nAwJ!uR24-IyWl}LdXPbsM?Hdh& zATs#}=nO9Rx1?@p-BtqZITtqq*7Mm-UR%7E`g+0Fho2ISYewqj$+E8R$Ls_zxb=u; zEm>W;U3#@YW(ogDU*jq&-q0)e`~LGfw)k@nJR`S19dgh2xQ{#VE{onhA8$2HVPoq3 z*v3XrnJRL*%%ZegPZ&E2Uy|HUR0U7QiUecV$9`~hO;1JfYI^-Dv4F-Kd2^w>9Y<4z zj%bb2rv91*@cpq`rzqa&`VGF!6O8dE{8dLl(wHa~rqFbt;#NziN(K8x?S}{kM=!;fx z%RYck{Z18B%(n+jmGpeIK2j*0^nqXW(gxJh&@Aodz;q{NQtg8z{talx-+Azy!Bg5Dtmt9 z4M#vSyI59##%;~kZSMi7`jP_Q#saCFo2Y>#blSN~c9Mu7tY1-NfSbV^Q-JELJJ{~y z15e#ii&UiY-T!IPuokyftA|L30SJ(1dh5RMU#b7Ua+PWAzl(@e?JlBRYoN`9mv3=0 zqinPQk5IeU{-t}PJ)#SoSVto!9IJ-$_qjyx6dnu?GY;BB3-_@{QYckM)VY72b6L9r zt<7Qzr^=RPCW8cV?EQt(HMs?{ zP|}Hvr2|o++u3~;=1F_87VN?1-{JH;(SSGNWLhL7zVy|V)~LT(VOW-O^j!zRwr6K+ ztQ1toHTY1eMrl%@~2fyLC!w3xZL+F@pY6ND`Rk68oI5}Z` zoV2Lk+m{>!Gz11cn{x4cehb!y@pec5*jk`@^283dhusg6aNU;?*E_Nmx0!J7@mX>- z<^x!;edtjA#6r=$=>xF<*J?_Xp_K) zNPHtM6y?=XiTHFSZo=Q!8Pt8zfOX@vw>^o!Hcs_lbA@z1!}HQc7ae_!I>mc@}ol-_S%V&4Uzcc;;_sCo$< zq9{=BTe8z7jZaX5OWy1`Sj1RzK=3&`U2TPKV$J*O9S&5%lGFXuoooj8*2VfZGmHur z0jte`&{S={XUZ}pa~`j3H-b_0+h>5=2O z^2mDF?hhvH`R7KCe1iHe4Kh%^p7Vy7g{`p)JqP1A)bqxlw#E;N_(ewfr4K?V@i83Z zVBrz7rb+(8Mt|KO4jSS~dt*fDtr*&xWOYzH!zE0s8&2#pR)>kVB#V)mX1YiZ9wnr+ zt_||mYP1~LicgTwmbkLpBj>X9Ej1KnDvZ_l^MUa2v-J6|K7|0MJJw7#6f1sqS@DUo za^3%+*?fq3S{7qPSM`V>aSnz}1rMwE&ThTXjRE%xj7gu>5)$`iIPAKFZm0N+54Afk&|@^iEv5A zI5J1#tCLz2#(_|N%C2l-t0JQI%7yDQbf|nlKLScl{#)e&ke3|@B$YoUS!XyJMv=&g zdSXgV!iYm2?M7_3PnWti@qKibMW5xNL!FKc!|}=Mv1P!5U4bQ z-=`ewKzWOZ03^Jr$KqRd^2Gemjq}9vbj;6G-!*eb?)Jx?nIXCeAVXYs9}p>1#YlSu zi?@Mp0*l}FPalZ`NCvdurntPCNqgv~(kzSTbaGbL>x;5@&tciRg73nwHy(JPZSurk&Wy|?yfJSNm#vb0Ibr3g^>Wh2_bv8Fi(74 zbl~IvibQy^`tJy6y6!_jB$v(?`0nl~mM#z5T%%sBI{6?L<0>-tc)SzOB6aBkd*{xA ze?Z#d4t=TZnC=~I7nclbuB%(r5+nZi)E2%pR-5j&PZDTS+-d?+Ny!I*K!Ye94xgB*IUw)HB?63y|7y zR@{H*9G+5O_=4A>DY-OkZ;_w_?mDekJq8eiN#1TYEMZPo9Cb|Z(y~(qFcQQ@oAI77 zq<&+S&$BNqCLul+d8PqRYfqC7rCb$yeO4h13E>+qQ+gMyb)k|~`>pWOlQ+q8Zm(vB zV#K*uT_16hX&-%4^+vDgHV|pK_Hhe7G2HjMc6Fa9&U|(izYd=te|-xL<)ts9RtDD4 zqC!+^`{m8+<4f)fqCZC6@COa{xqz_TfP8f)Cr7#V!%27aroX(t^za6wAisqA@8E{x zB5E_U!`I#L(zyZSKmpqvsg!=)DZ;wV6@iYutwl3sv3k$5sRWui(F}&|MAwvOpm-Gn zZ#)wm%5wKxX2Tuo+iVw6`OZw?qgQMGo}+w0eGW)t^HTstl$qt)*wNIM?cJmmC@=^> zawYiZnYR2csaIx)GRxxP)1G^BN~e2#5`;3pu8h}YA{S#>-iZOhPlYd0P>n{6qonS= zBjo}NT;^W4YTK9AK#Ap6a#t>GU`gKpG-qs=XCtFF0hlsg?^ZvMU8)OkXzRj9LKAdD zjNNG+`>ZcwG28}h#f#^QNI4>>RW>%Q3isFF*H@d{_Zv|!!Jx(n(By9;#uh2g01mh<9NN6t3P zZ7c=dRWE;4P5u037HINfB0TaM$a`u%djlAoTI`mFPti_V1jzeRe!8q_*!*koQoH14 zrvffm4WDHE?hV5bJYTdzHfHi`Z_oM{H!IQw&U)3};z0$|NEd|`z{4?D> zwoOyh)>R@t8e^fYsEgw+U=yXU_DGTGKpo zQgNfyz-6pY6WF&?`sfh;;`xM0IIl@obgDz1X*%m|tgH}~r%27@#CivhjiLASn4n%C z=JQ=a7z_UFLa|-Nu+JqzdATd^gWYVvJ5q^v(Q_*`r!*}T(kz|&MUQ)Ib2dG%%4<;K zC$G32<2je&x;16cPw52}0BYTpx0;XQp5FWOnD{f0mA^;2*owQ&|Cm z5{5k63Xw?;bu6Raw}^$#rPFTIN_D#mXN}%IL0`Dbit=f!tc{U$^u0wZ#PLZ*KV+Ln zYfMw}H$T9KI0OrNKy7>}sN;mnY~*cCkjlQMN-IAkA{|mV=jz^NpRL5{_KENz*1?I% zn_ryF*757P6$2%T+@L=SoOdn)whnU`)4%r{Sm3^to?Q3?cX(Qt+qa9nO$ONTL@5gCU7vOMeqCpxBmB?{I5#nusgti zPiX-)YhzK(gnkz6 zzjs9eHN5;XnD8;4=XUVlD9eAjEH8LSH9Fs@v9-E!Ms)u}sq-%qE%6j6$}+5YOMC3H zJS_j6ln@RkSnK#bJ63up0zQ(W$Xl`?K2OZ;NBd(83$t8 zL`faHo2kzI`qWIn`t^H&^)m{9MzRx<2N78vYjm=gO~55B^H8yHUi*hq0#fXpvnO^Fe)Fo@50R8 zQ?O%ick{)K0RO`j)5F6BblB5JNQu$Ayz3#f*MZKlx?L*#+~=K-n_Bl>m$)H+@U(xy z)QN!hoA=QusiTFi)O);FtZ`U5bJlb-G_d*73&tCFe0uct6z=!cnq3Kg`%i5B8(0MV zIwMHf_jkj2-i!}T^sM|K@i}Di5+J0L1BM`YSk)6k^HNq-nAU2VfxoPAZLil82?{to}6se%c`Fp@eOj=+i9@{mce)1IJNNgnQ^P{_|d?|Ne9LVd6<*aP6 zJw_qcDT(g(zBgeO&CN2$DConqmjLL}hcaSoU-@xXNx+;8OS&QmfdO>K0_)w20hCz% z4?J`aoD3zw$)HNbM3vq>LkTL4=*Nriv@+*JS_=$LZv;W<22 z6}yw?v2WK(`w+y(%k^O3<$Y=Y)}4J%e1bLSwYw*el_%ZB1U?woqX~VgEZvVclW-NzPBs6;i@UkSCFO5 z!{fjI)BA_&Agz9^ZaVjc#2CRwPvs|22|)WkH$FIaeXW15FAFX3B%q>`R`}Gh&6cr` zPGD<6o#*gmYmY&E=NJQy#fay(UOO7z00$ESw{OAdgCidvr;HCXNl(8Z0ElfLc!Nf{ zo5#NAOTfjV4`l$v<_Eli@g3S@;hPh1PQsgjPE=ZH;0?ZBJa-&;Adqi%INB>^(0(fRm(fA#vXBBfzMzfhjp>yh%+-x*i-_UxM->@T?O4)B1$X8D-`ns z^pdL_kdM>%DUn4&vzf1)ul>>7-}+OePPA9we&zZ33@=paIO*qe8JM&Kl((QxNT_^ z28u1-Y%N#A?c{mz$KK5*wzckGDBN}DCCeIm>rP^Dk?9Q{B6vHYQo4NdxReWHiusnSJBdeu}^oVVF2?JPZ-_o(g-Ly?f_;FEvleBt1 z9Ifwb&Na?$B?WyTa3|E4INYCzPo&OF;eKBwVpaIbycF2p#2L+f#oaA``SLWycphbp^Y zms0)D{z5(Uz6HnWMO|FLydt{GrGUR(rpR46%SB_;bmm8gbF_=!m%?k!!=lHnx z!^|5R!fPQANdV0&|B2>ol%Jk}+~arg6aGr2(_&Ddq!7b&@27(~1s+JvMnDCZ^X?<1 z_qW4E%Va|x5krw;n(zHx_6NYg$&8erd$lNapKjq({e{q@9u7ar(oOnmzBbx8Vt27) zfgLMu-*XudU`y9gV;l@P&Ms!VS)e!(C5giuczh1Cl~{WZcH(8ok~31idt_4z2@I_7 z?D;M)wVcyL6rfK=-#fn!q+S@HoxKk`W=`8vMx*;9hi}6cMAPsAkTE_)#TO%+{w?QU zmAi-{Lrpep?dZ(SpWIM}ZAL3`;mR7HFvnD&$=IJqJz4zuge+H;o&?vmX>Pe$s zE~r&b7Z;qaQ0gt*x_PpFjKh6S`x@d?Mu<$^dht8J4gl^H0CtktT4AX5S_lEcJIj(_ z(c`lyw3JO?EJDPIuCMwc#cT?%;tHDc??lzy9OY|~1BCW`fd_49T`}Zxa1+Cy>$Eo{ z|J;d6=7tjEiq(cy+^Esf-iV-deN4~!^ZH@|m-K{%mr%A0HzF>f;!A>PXd)PF=g~&J ziI)>>hG8Qk!2Hl_pY5HP zS&ZRRbOYq|46$Am3CPRlpYr0lFi(X?7TQ%8^AGjL{>Y8bgz&4zG~lgJ=kw2^258R zyk$XelXorjBBh9f^gNF8#kmnX{2N@m-<8w0A?NU2dY4|d9}Sx%vL)Usmj$7HZ46FG z#z>dr%%3k=dyBR=3n4&tVC|7%}`c^Bq6nN4eq4d2&@eK40- zP12L+t{Qo#pG}0REIuFFNbR`9LHU<5 zYi*~P#}-eNw%L!I`FYKf`~(<-=|m+voAf>0_M69`k-jt%_Aba)av^^2?y$ z@q<@y7^IwSzd9CBW%J+ufGg9K|MI{KFyJtHLi@r>5>U*rQpmCmC>Vl;HUSwW87@)vK-w&GsZXfLi+k0Sv-vDf@#1sh4E zJR4-jr3fmi-G{*icz^SiobHs-Tb#^besk!hN|>(e9x2NBIxMn4cM8jUyd z62F_V(@*BkgS3{c#vIB>PIODjo5fkTS{Nn5$9INC#j#F=P#%OTJrCdRnevcG(_*<1 z=u;`bL69z>u)-; zZ-fLa%H8UsllIB7s}s<0Fr#-#F4v;!?D5%N!@Es-TFdpRMH$quq|KJ2joc$&r4KDt zjQ3X<#L=W+bzL;N2-(b4QqxmuJC9d7 z=l=C8H$vV)tWRs!+6eP66xU?ss#$!PJXrnSq+;$fZZW0v!#nGJZA1;Zd+iV?oCn>$ z@_13)YC_WeKI2Hm=af4uwIY(1B0%>@G%u8DALl(5l1%Pom+&anPW7hE5T9#5^U(LW zwkwM!qt^buQjm%&?BE(hDqqhkbc@O^9=8WWx!5r=_SXdI*p|o!2|q&I%2=G-30%2G zzxA}{+Sa(YI`_@-qxO;B;k*!uu02DqC|@wQRsF#-N1sJEa+%)>k)1r+sl1<)@M4B| zOFxC(E;U>BI!0AI&ojms=~jU#U3+6gZuc3W@r5n-atyA!E)V-L18ut1SwC-iCnm!u z8oBFS4$_hZJcq;Oy7F_vD~KBfHr7pf#b5Zdq+ajdzvI9Rf2gP7%5U%V!hoab!nRe; z?8K7YM*YWkFLF=y*&RK$>#tebWAF2l87Ns%%_G)yDUaOavw%69br%^lRY)Ig6N=M( zH-qiyCAYSzR9CQmQg$_8R(gE^qc|HADq<u@}6&*5ho z89Y~?=h2WI+5p_2_9=Y4Em>r<$g&Jm)}FN`^jOTI76(HdTA;)bMnezQET;F+>+sO% z+Ky2t;+M%6=>?zcGmVs|7J(`#=$2hH_12g)MI<@1lk(TSMN<(n>O7!}++n*W$np?+mGg}+UPYAh)R2FOl!E@YBEoL*;ryJY*-!PW zzl@0eBfKsP;isf(e8y!CHwP}(rHxTA6z3Cz^lHVdr$_nl$<+o{$_`@^K3cnLHhW@w zwt%*he-5Q8`d@5)c|4SF7k9f#sBGEWPzjZt(5hrhLY6V9?E5-dhO#6Hzmk2+zK?9f zkeLv&jjUrEV;>AAh8eTJx8L(T@B1w8KR*8Oxn1{lpX*%be9t-G@5}L@$#;5gpCe`* zcE99z7E?osOvWjTsq+|eZji2SYnnLP6iQyKHgU~pPa@(BVZP9f{ z+=I0I(|(cG5L^6hYmvg=H`Wyfp63f_#|0saLo|xsMq7j36LUdn{XP=~va}$!*ujHU zW}Vq};UqD6ap9fQYlO0y%RJV+Q>cxdnWgox7SqhD+X1Y(`Mj_OQ*Mgta_w^s8Zm2< z7D>=5L6>dxPy^EPq2jTabz%c{$$n;0td*6du@NFk%bX$)*6xp7u2VX8KjH2TwUG*L zIBEd}VUVY zm7<~XPrl>CR48S)sj~;=KQ0y>Q^)%WxeoIHExT}#7ICoY0qB&KKX!+ybxV!5u%Vt- z?CXRX>H|urtK0=lTK~h$v9e8}7GVv2#{df36z|2J3D1_WHWTnR4$zZK`|8 zS*7zae0|)SP;Ymyzl_9K>QCpJ!-taZshU6N>DxCFQ@CloJ|yfWGm$FC6DKD8zV^j= z?b8b(^mU_}>da#m;YA>9`IbB!)1ntW)9APU>!?c)p6-j9mxC!ITLGlWY*nLG6sc8UCL z?`qUf(UsI>+y=B#2Oq!eMtoqf+TOXGT#BA^A1Kv33;WPF%u&hJ{T_FgB0~H?@N823 znx2|nVO^|W>$|iYv!XLW~ng429b{`}P0&#kIOLicXO7YKp<2%~;cL+Y=!CxpmR zVo)1PX(e6I}axeo;DAKGG0@De?4&-I;S{4B;N=|f zow~O^RQUQq^SilS=CI5%=)aLZ zDKA4>ccE_XPI}p)#_*_6F1VoFo{!o6cL2yh)%>MFtSXdyMg7l>n%@Nukxbj<2tpKu zv+oAO-XJ(*8U{&==1i4!wA0u|X9l7dofNF+ZRGq`hp)#QeOGqnb6g@MVm^8e98!4T zfnrt994-lW7kIgRu1#k!xi(CTGHCTS`efg|M3SJ!0MZ^ohi`Z^lr|{|Rev%(JoijW z<4^my?(J&R@Fxl?cBQxMA8#LPrVo#MoFIq~dwsUxLD)yFyEWQe11k)uOdV_oWpshDbEJ)RP>lue~jWIUD9|naT)n;q;rdAZ+KI9lVrP`Nd(P>aqE4i<% z>XM4r;Lwt{hJs>x3@>`bB7im^68`mcRhk&)vKu_Ed)TJQL!y@kS-d*rcVydHBINVc zng`7sXrp-h$Jqgod`L7aiNa$RD)9{$`D7Ed9cqaQ_UGG)&$}7X_64f=WU*te80-x> zx1`;6_?oC~&tDnp8chKQGUBLy=fEQP)t`WtjpzNB+c(u_{?vrKvB5y-(f_H$-#(4N zDB)fflDz)d(2hKj18Qq53x0giGSd)IyqnAYFn;<%I9(v9FNMr8GCZI;jlPO`{&`AL zFwPUr~rwyf~MH0_I z=fvqUUT?J59~gQJK(jSrlr0_NDMaugld2(oM@p|Laz*W19RJoL16@Z>_>%V`Te8t` zxa-T9n2Oo^r==Q#6H3NeA*|Tn>97*YUs>|%^0$BV9f_&#!HZoK9};hr<9n#+c5d&?>8jmR&)ehv3EG0%~JY)F+~dB)-`Cj%P#6Rvm&qZ@eS*NK#g zGx3I*?3Ex)T*X0NhvU3Zv-{a1)b9=-`g)c%9Z_SddKNI#DEVrz9m3(8{Wv!Pcllx7 z;3{hV17f2U9p0d_^rFIo90E}823%wzxy8Yp}t%==}{w3?e znMdPteH0t(MkbiuqXf-dO8?`{3f3;l!pfX%{{YQOexwR<>K)aBgfPI#Av+&Brxd!Z zp-fHN>8`ckey= zc&1RxL~`^gs1rUzdq``t#)>t+`erb{OA!VtB;R*Qk58MLpE}m5s0?6=LMJ}BT)P8P zr)Yi%Kxs&^DskK}^n5?H-DS=;aaf{^9<} z`%ima`r|?37V7hcIx>0d-XB+XVkq~??2Hl6V>E> zLZB(*ym?DTRUul{oP#PMV_3{v-j5)RSe@sSPOPiemD%4exO;KbISJ`=djIgVkLsbL zqrDcmvD9Cgcjdk0lgVyY!b%wnxpPIFS3miD&oq^>1;gu%ZbFf_RvEh}UGU&Q>KnPZ z2Sn5rzE^oKs&S8h)xL4AD$kXl9LF)9$Ej`{NUl8`!tC&@nb(M%`$$rCn)>7E|2bmB z%)w^W@GvT*$RryAZ}NOT5zyD*Gxs&L)u`y+y}pxV(BM0BYXl&X+7m7`^ua`V!cJLw z_2SmAdl4dk2G&IT20xLAGS{-boa<{l3K=Ihe6s3@4He4aE$?&z7RwNvr2QvsEHk zhg+h>g1XzC!tFvp#V2NOT7mv>Y-E%OCcCpS*6RKgEtdV z#P7(6PzB-ImsNe!#lHAT^#=P>k>L}W<2SCE$2st#+bzE5^`8L$ z9QK={BgfRu(B5BtrJ(p<9#Vo7Sj^};i0mSEgaRBy;3n_uxprz=r^+j-)S6&1lYYZT z_lxFuLcGW6wN+{{KHvH}1~e9M67%jdd7R7`whB|f`pMP9g7u1CaBcmlU>q^k)7saj z?1yy%S~7L!LTG#KI-k}`lcJNv4|#L1B@;hvZ~!*Qiv8QST>E!k!-CGUp=$TDE2lM1 zb&PBD*H@aJvRYz$+2Q1i3Igrz4fj9j0cOCytG4ddtIKGu2>b1B#Q1$+I+Vx#Mw_-@ zD=}MD_8Ff-;DiUmQOY6XNAY3%S51!%xiplPGGl@eyGw_2<(~?gp$vP`!#~JOCyf<7 zb(&CP%me>so~gk6m;z`|}Biq1Jf%XGr^zL%IrLHbq)*0NmX z3{}mgA1eRJG?_Om6!Jb+ghtWDM%iIMME@e2qBX`X%EtwkO``5C8f>iKZFm?Q8lOVb zTJlr%y=fOMYpyJMCyG%wwJ9G*jZku5tN?98L3d=~gKyso21Uiz2Cw5^3GS3D^VgIP zuRI<9++%7}tvP8urB$Y|es#t*py*lwADSc^&SEmQNtIP_FD2e`dL)0p7u$s&$hkQnnt<*lVop<|#)yG~w{EFT&7zkafh|B(2yJ-$fi z9#rQ~cda%2>!a%;1JR{&SfL`97-_vYg~i>aSPP|bd1k*6wCe1(;Qq3aPZWj3QyH|I zmhcVXtF-bzy01H$_pD!kMP3>0o$N9aT_lD5b_*o8v=_KCYU%xjFS6Y>n4*}a9S#uy z$0=}47TC%_KQ)uwD8(+=N9b^XnfQEmfm2n#55_&#@Zz7LWlk65Sv=kerW`E+)|!L{ zX1H;WIpEL!s_?gk$da$(!5VTr3&i6ZV>6@2Hc{gKi6R^rU$EK}nHP7ItR|J0 zyhUw}?v?z*W@x2BF%?p1}`w!eM>c@UP3{KL!5v22i+#v#cISTgRx?lq62IYod{2=4hCS!tl{PGS( zGFb-2G_3Ks(V;Caok7n{N6k2U&YoZcLp=dI8kQ60joR>V{rR=r4}k2;@PQiaqV#I6 zeVDJ!%3#`lm9f;jNPudDbl7WM+KZ)6wzhk)NnO;u66c_iEF-H$FQy(8RL*Xk3ALCF z4VanpMulXs{CpAx6NCK57U%3~(@+=G@8D1RbH5Y~mGPW-ubQO1Su9<*Q>m@a?$ItL zM9c2C;7@+#Xp)15nblkrRH$WKip@5H<(H}$5V4Pfv|NT)ZG2F|@`D@ftq z!X*QgEM6;@%v32H)^)dv1&+(YqGIGBA{mak9N@TZ5k+pJxor{IS*~M2AI=nIq z-kK^4o$`6Cpu(~5Pp6E{^)nKUVTm^E%+O)o%y2)wjFOsV=FfHdj*~6^ZKyU(BF9&d z?n;W>T&CZf8-v#K9JSByJVwxJ>UK_Evf`-o|EqVb%OLxDk7%gneXFO;1ddMbV{vq_ z)R(z{jeC0oA+E%-v$Fv#ldM&j?9omMfixURI{c!n5i8<2TW09ux+d6nJm0TCb`I8YNL)Rs^JQ=;7 zjQ5oMyS3-Wzgm0Da&M)&W)KR_=iKKyuNIwsXCudwwmU82qvm%y*s3X&kxY=RaG`QV>H~Lf`Ddgn7K(R1At;m4V>l&L zBUV-<(mi1v<6P!`oNm|Y zUd5k+c6J~leqr8fsj}+$ZGqXVS8*uy+k2?lsPXtSvj;s0>m58QfOMc~3LW){6gd()p+zgrXxx1=RzM#CGS4ro}KDPS#x$a2K{m9{?441UaIv8YBIUaan4C- z+d%zN7+}#MI%7Akz6Hza;;;R2WZMyZrJ>X9esaOagAzx*FmmlRKid~W>i!gM;1Czr zZ0ZQNGA5mFoxFIgNp<$7lbZaV87{seTGiBjw$Xs?8RAh_YauV{7lJtvo-}IXI&_z; zNPyJ@I>u-S!+@dvCs|$rLI%m?nCO}t-GI(w3_b1J=At=Hz%<)iv&qkZtv1VJ!`G&_ z+1*~}76XM-W5S(ZL>&2zt3Rl0NxW^ZfAM?|mqbZqgG^RW%i9y|D`lWG53ylOCE;8C z=v0wy6m1(*f4QCXz4R!acKpx1$?3@#B(By<88Hru)qBqk&LU*Ai23Ne)cA5*;F0co ze`OHmflwd>2nCM(+}8R_V{VMIcg-X8C_I5t27|`Gm?nBzztS>u5#CL}W`LoxWlF_U zf^qHebv}>5z>JE}nZY8Q_fA)xVY2TD%E$-WLrWKjRJ&E6TsWy@J*JU>=ntk@vYPMJ z&v%DZ^|P1##=x)lyyi=Q_Zsk;k{L}_f3)9vE8L&@h)i=XZ6BB6Kj8bFE5UGvkOQJyPYuBAY%f&E=CtehAiPHT zlGHi9DdUwwN2~)T58VFz5j3z=aCv*0TAf4Rx;$nR{D-jZpKeFZ5{@s ziE};#x)dTN>`&l3}S=s$bi+=6b$JBpu0r=N+FPgUwu-D49h}FRq4{YbLS~Xo#!qwwD;IX zdnlZbb`w0-iuk$m3Fs8CO#TUJlt>JX&V#K#1FRLL!LVTHP6UN$>eALmHGhumulST# z=e;mB2Wv5m3mriajeF^uA_CC;1<`k8l{d z5XlJ`=bET@DtcMHBNr5g^a($vPX0%`4{U4iHg}=c@GqR(Sj@fupw9DC2I{1t=hq*h zi^00||K}aW(yU`=??n16Mhj&5#-5UGRHMP7hI)+}+RFf=*! zINM??d$xd<5Z`0!&zB6I&f}VQ#d{aD5x?45BN{ zh0;}zl5G>02z_U!Q;E`5jnzRlQa?z}uVO)gPJZ#-7&u+X$y+ zBld984eyGGM&(>M2zz;{5s`&$jALi~i6p%Uxa_XI4ur;%s)lD2E~O}yw4_(shYALp z=C}An-&UDc*Q%|n^nK+67@}-GxAUfx>>b%6c)oz87CU1;oQawK#@PnZ2cJQX;Sn;S zfp}$gPnNoV%V86CaE*hPGMY&amr%yQ1wKP^5xu{cIc-w6=_`0#*w+vn{_`0wr+k_z zi(7SG8|mLJMaKHD^`ac<*B?HA7dq1Xw{0Gb`xsTL^`k^u+}%(sM|Vuz7yOQY5WLeb*=&YSj-A9trTK7W`u?6sK{(AMFJnYq?oD65eM^u}IyGl;yi`b@3o?dkN|8j0y=&UclQfH2a1 zG#Phh92?i@4fK32*(t_O{3G8zb^kk1h8(d(U`fa^|rdo99y zsM6_629yy=eL{_#0?>Ep^bPb21%!|tSUs2`0DJydX@UUCjzsQ$QwzUh|y^_kkh9V4;0~q3xsGRCqqmY0uYa zHZ7{bxVMQTa@Rsb1{woSCf{4xt8DEKn{s?u(T|Fbaou$qyn3XM;cqAwlO4)ZDp%NOu)l6tQzK-G z^Z}-UXX@Yplu{#R3~4XGr!dPwCTc1NpH9ZdqSO`T|12m}rp5&J8YQ56biZo$ReyHk zQG#Uxu6Chco@hMFP}dl`3m04r*2r&pE6NT&m&ezN@Ss0UCS%M!bqDS=RPW!&QevIY z{YO+#pvSo;?lm?o7rj&g(Y|i^!!+AfqbN1XoU8FSAf$O@V-E-gbdJ zUtNH75gr#^1U}t+V29~wC1xetP}r`Nsn-gC8rtg%_Bt`Bu9e0CKi#Kh9dWB7!vWMT zsf-@w)$0{aa@TgfYXF{(#JYcUB9|_w2>cBS_*UxT)V2ssHAXJBhFVBnPseUv`NM_h z86?ZIzS~^QcSy%rXH8)Ecfgr1mY)zvPYw~r`u~lTIS3#62tUPy==>Wxwj{q?lG+7` zI@>%?TZxhg;YR8Ymit6^t0(uEnl&kIsAbAPJL^ZG zB2PJ&8GTQeq?l%3kIDjR*sc}Jgkkm9Z}(k9Qs6B65mU1hj9sWKdm>FXG?0#5!cvNw zio?O7wvU@76jSdo)zmQ4Lu8=SPwv}N9POF&ke#yxP+FEjHmHB4yq}gv+pX#?Q(qS{ z1HEfO+V>A8|HHv!y*{w65VhfM5Uwj!H2cLASh^?$o;hbklQWp$_(Q7w(uzQq|6b5g zs|uJ*6U*2fPhM&oR`3R#%8{DiJBe|`<0~%IS>}2}H4%ex1sCzrdbfvu_y_!rNeljK zvO>oAD=^(JmxrFsRG}Pm2n@dQvhWjA7te_N;JcPdDdadn+3WSv)|mP!3fXSS`34(b zqFXgZkk(#A00PCVDO75NkL+%KWcC6dD``^VTI~9dD1iZ2r*>g{bs}e|{ile{rcML6-F*11 z_o@qTbY`KKVa2gT;11b?=umA-oOt)qaA9{IgTRiD->Jx37tK>g)(#(Y@J>~CVkudN zTS;Ct@9J>QfD*T3p#SriHnw*CmYh23Md-pK71QiL3PIS! zHS>c_hco!h#EP3mvQH@^fhfIwC7Dv}8PavX=rEaY(I}Hb@Ic^PzfB7R)Z&we6&7mk z?_`kL^YF# zELa_RoNhr3#N?3=MQKGX#4)(t4Vn! zOkDksq1R$j9HRbdSvrzkTE%~vO55<27CVZdYdgnC@<@B(abB&)fbkIW5k1zDjjcpM z2zVq|-6AhRIr5R6cH?}=G(XMjl!NwQU;*{&wM+YmVgO2AtVR;JD*?D3)CoWIlsrPW=RAa^os6gl_HnP9gV!ujYw=>fs7Gd z2N&PbeREJ%v-o?jw{rPxB9U$ROjpjzh(D7mxI8473(gF~zbi%zjsBzz-pL+95I)l_ zJCk9bH$z<%Lw=@|^=kSEEEulZ6j5(V_}i<{o9!Ux@mdmo2OL@w`qPuF+?>f+0?|Hv zXSCPS{&KjFsVGa_q&{U;+=K|8#BZCZ-{&$^bjz8sp5NA-HEaUdop-{Tf&7OalIQLb zj95emEPal~(31x|4r|YJIk49o^9DD+6*aHwP#9ad_~|NXJ9TmGF(b{y#e zm-!(*ehXxMc}T(<_Qccc6*oNItkMi%6qw9aM&E6pb%x8j)#}6}fPs*qd}XOVsZxeg z^&6Q@CTT;|zO&{J9Yqg~^JYkIT~IapX_lMXD(_6S1mwe(<%XJrzTn*NH+T z;pT9LAHTo;82jvI-$bLK&B3*ruE`j%W#HHSt% zLdrP~ioP5X%jjgLC(uLu<{US~cxzbbh){weQ;_U@KJ;|{(-y?4L5-|ZDDpEL{bUV8 z9qr*6!u{Ca){!oqO|6$$5KoY+FYR|X6=yYX?#~3jwyeB^vz-e`mf=$-ydt(=FoI3~$6w%CWXn zDt~!bxxJY&I-8`fntjv%E0y4%{vZa;EH~Ek#rEGTsGhl5dbU47aE(8!D8tLvY+jNL zmG57$2;a8A)OC8+?9TV+&L%?rU2^YqzUf$b#EacGTAz#gHg!I{4A$IZ?bO!S$eO&c zj-yzo3z2P1tR_UBo0$!d>6<@(C%FRA7&%4paGJvH8`(*>j;+}i(>EU}T^{2y@@{(D zqOC3jVSg_=%^p2o`XNhTf-)4sdcZl;B_VeUP?!g#!=<;%KrI2U?>=JtGYp{KR@yGX zyLY>cz{%oJ@USKRZrqwlPEX9A=4Jbsy|!|C7))NlEp8QWSwnRi4y3NtG^X44rM+Ul z9Tf815h@pnqUjxE-VD6D-6@%&BP$n}IozUX;}lsgag>%*aMrLw!8gF!RB~NnCC^nm zgSB*THg>#r>Ko6GPl84&5FF)M%ZTm^eoA}=xR`yT+^w#rIojU+SUBlAFRaXSFo)Vf zK)kUg$p*783?cOb*s#Wxr5{L8o&Z;xPLJ_xm0#2EtNU&TrOazcI1dN9)-!wLl7|$p z(6(-Lk&}y)z9AiL0=UIP0{DKI)`2e#tK#6Lzua_7}afq)6pUS*nXk#|=%_HA-(z;e5hoCOS9SJR2ZGiQz}oiesd)5ri$Lda7<-XDK=P znkq*3FX$xspM*wf#CRJFO4GXK62-RHmIMnPoFbH@+O ztv#@FF&q(N>1B=nHU3Ry)~8WzFVl9LO#-{3+W|!`)4X zTcaJT`di*e`s*<5aq=BH@j61~2Pee`yKH5?&+Vnr8!FSFGaBaGF!Nc+{%u9sV_ol$ zrudMrJ0IyP(zu`}&lEPoko>J;hd*8D(1(CtF58b^X*_LYzEBd86>0eU9o-R$QqZk0 zuzGud{UsNzNUt;eCuy!WWg?S3YE1p_ zo?|xuCO36Vx9OPkuV{2~)%R9`s{4FL7UZlzKBCib(^{s|C)SbT!^}mpxGF-DfmHcv zlk=6AKUP@jz0?p<8S1d2vM_j@>*f{un%UvVxVOdL^n0B<`^ohM)5-EQxB4-gx^f(^ ztxA|5wf@0n7BWbJT@3r^yJO;h;XJ6yhuKTdEgNTSv#xhRH2SQ1af7O;{iX9N2rW*D zbd8a9cBA*kf>vHG^dZh=)FQ|aqMc{BPJ!TjwvoOC!Sy!yCK=JstkEZ$wwG&(c=5^# zjf_)h0Zk)Ps*q`Gf;0bFRijQl&#YhZGdE9J$!6m1P;RIf$I*Hge1}#!iteDg z8-p(`-YBbqt=5*Jcg(P>-mm7lFxRs~Sc%li=7{DLw+gq06)D2Fz&-6$bAX&xCRoP5 znSb^2f^kd+WZLTK+M1Gb^Y`sGJ#0Zlh5rYO{X4NpV5kED9$>4^~q@7Dp-TNfchvv5yNL~lsRRfK9B%}UY_Jtk7h>!HN@98`kAM-J2 z5?%;MxWw~9S&~=|zwhn{st)`?6}_5Q}I2N{04 z3bA)m*6W>S}=&$RYh%K0YkhZ#L-{M z+mom$**~uz{7Ce_ zi}hZ#v0X?Wn2)$%J%6~_tX|Cd;bZ+0S)3k!SXmv-dxy}}pZ!gvQI#mYf=48kb%?Dh zFWAvF%cNpp)8m0X22)+mJ3ZyUk=0Kwy?3eZeq;|sj}7#rrey5R6KLoKk;pNcVFmA7 zbz4i``zkUR%6%x(tWG~9=#|H*$adNFW6At$$KsP+kBZ_wkd^1UAWaRSdVY+NZI3q> zCTedukXPoWr|&1*5H@BPa4x&Ws1?d|V2KFdbWOWa!d_q%-dSJ;D5qn|lBz}v<{IHX zAoLP(qda8k>9EoAJLO$VmsnE+%u8;+w$;EGABxm2$NobnZ|&QEh*f(aGK{1xd(`9m zs{_<9(q~hablIgfBJH|whu-S#^{p8<*;#X^Ja7jvQ%+14g3@{eW$L{ zBWLF3Mi}WZ-V1N1e#klf4eYt2i^m`=S1x2YDgb#p1U}_Q%(MT}oE8^$>-vF1_;yFr zjHH?Gxw;?E)oH8tLwOJ35$C__080VZ^S1HIEl(Tsdh2ztM;K|)Esxc#k$z!vop9wT zM8F5H>9mo}uBHC4d0X3kB^GorJ8t!Or+;5zZrKe#c5b7kcoy{ju-n5i{wE-)_DI$A zA^b?rqf9^3=T?)yyjFL&M8>q}Q$4Ws-1_wqkyV&?%@Sd<|2K4s-zYG?(V6ld?O^Yx zX=RSnAb@IsrLKHGhUTS?pn*5E%4rX z8>Pg1#7_d2MP4eApMWi2I?5*lJiPoFdHWE4T>LutR7PjSiyQy=S|@LPT{zIV-;FXq z(HQ8*s}?$s_$>eaINSHY%LkdZ^ceTqX$b1vJV3ekomQl9X-C9N8wpYudtp{D+rwW- z&ox_a^r~r;PO)o}DDJWQ(p*A?g?c!7++ddp1+rd24pJK#A*C)@KZ)a^;d3f$TrZ8* zlyZyj_aW^vjM;PE)AId&eKDmUqfZq}9?)>Dk6iKd=BOE$A~dCk&_)KEEs=F9J((KB z%L*Y|2K?@?2}Sd#-j)Jke%a~L;F~Q0i&}>}MqxAGe>}Qz=AZWWw-KH!RyxpV3!mhZ~nVX*^fIn5Xzw?ie_aFZlFgf8ttSUv>79n(O~cYC81)e+xT$_VIPa6j-q+1uJitX-}C73`+Y3|>h{21gaCg!w)$It{sBcn zW4)oyYM!$b^wPdBB%iy2IaptcZLC$o#=GFBQ{a(sUAIdvl_R}VUd;RMrdrmF5;aE3 z{yN*bXB_{#njgT+PaXpn@%MUVX7veUKdNopjsWBK0!iOSw@T$Cf|ABpBnD}`9hBm z?Ctvdf9-mrV)$q(xy)WIF#f4tAU=#?rxb>Ix%AzuGJDCt9|sD z=6~sb;SJV$5%2~tttXwC7s5!YbPEs< z8bS@y^I3^xPAy5PR=Ndg62kv$18%ABt#9vB|7{h(`ka!vlw|8&H=Hl1%AazUtz3#f zik@F{@Y7T!2KMGGeQ|%#P+{b0)5CMXjORb?b>G|bhmWFT4)vsF%-rO-IIR~(dTm&c z=^UxU)wh)H<|wCHUu1#U`~ce`=@`Y(RN2$hV}AxZ*`~HRt7U;GGmTVPE7w<}&!DCN z+blTEoK1(PQhwDeW%r{jZ3DU94|P&YujTHE~VFKkz7yw&Y z5~*|up)LxNIsg*n03K}@@!;K5M|JkAI#cuOZRj=okljsbh1EHsrLszt5^&c?tG#A} z`r^n^1zf^cJ*ERG2Yu?3YG%~5twvu0EZn`iof7VF7euN>P~S)|Ko;_Ry0$0yry&~{ z8g($R8i{F!;9Axihv4X@aX`!h(822d4lwK5GZxZ&>v6Ir*XVy*q|;5!YzFk3mgGo- zG9D>)Yg<;6We0|HEtY^%N>#uXAatRmg0-@p_I%^MuaRIldFJpSApI6Y#R1Hplf=MZ zYjzmvmA>0CSXTR*12xQtDp8Arxoi9!cV{tOvNGADgQ+UDlXPrkOfTT6?P1McaDers zx?fbg0tZ%nHo344(e@oJ@^$er^BDcf0CHA$c9yDLVYVc}nErv>gKUnwzGaM{usZv+ zSv!vuGs*Z35Kqle1tBT4kE^DSQ=Ta=oK3&Cbq>$Iy0gDC->l00T%92@DcH4nizfn(mLQ)kg(4~>eqSKm| zMlDF#Dp7+JQhr}cPzsYg-BsR1UbPo$vLSx2|8XX|an_=-f`oE0!$CC?t zNwCT3f5JUUFQg7H^7J;AcMDrWRNI1qO?6j~3>VB88d}>74V6{R4kLwxcOrhMXB3yt z1T&UHmJm4d?f5nR4EO}e zBgA(!?*RK89+ah||8_3o`oZIJCU0%C-zKNrkJ0W+lc%(=_NApo!?UV4lAm)&f z_GDnk#4QziL>n)UwcSDm&7*NW#7Cz;5A- z7&H`WR$a4WJg-?2E?depk~T)4QAB?SIFtu=JrP^437HfFqWFXR^E(TGlA;2=Zar10 z7xnrzng3IfL=ydV`}X8I`59I;@b`xMmpdB=xiw@V)yq53`dF1RWzcxD7A3629vyC! zCQ>PE3n1iliekK~h$lKyXhdDU(2E`wkC*L8f^p4J0SQXmmi z*IF&=9Csw^CEe5Gpm{S4Sh~DdPJXu8EJ0d%%5@^&%j|Ws{K@jGs^)~%gA{DLY1xlT zQgoZOo*CEUvRNOF%mu*}%Q)2L=$*VBkE)e-OY%Jj~Me zWdx)3=rQrUa8IMV5p_1|1VP3^$f}5t(B9j5c$( zKPBga)Pn>-TBX{48k;cX8!{sZ4sokIvD*zd^KcfPe5`rZE1Yx@$UvCo)$8O`EXJ(P z{PeCHjB?UlrP6}(#g43f67CM&wp z@CiowiaMI?j&0MVv>zZ!nSFRjiK#M80PidpRH+{xJ z#?jWQ3`N18E@=ce43}-a007zgxeaXoVJgOOvKn`UPxr zLWPxGpTn}%rw%6tLR-el`pU=SmrDp~3zn0@7{SBE%qVN&$S=;C(4p;5_JGu#Z^ETp zH`h}3Z&3Hs*IdyePXwNZJddicmkny;J^nzf3Po65Tou{a9Kl1MzNZ1$t@W=8&+WBO zCb?#t&iobMOk1kE$9mkaDG*;@E0sQh=Ko=s>J^ph1*L8sp=eCkYQ8d3X&!Y;hQ`G& zX|6BC75a3BPq{>v99L+QgG`K5kC4}eBlEq&NVSt-8D0l%&V#OfCcXYc+e6Y^KA^2- z1niz_3FTLLnS}3Y9qE@(ht)03xr!I$%-d>>97CU&Ypo7G#FjoCWkxR{yyqU*^qJXF zI4=!%QWv}qx+ULDck!-|0~u;{YVxp+V*>oTa!+qiY&~IK6P0LD<#U1Y8t$k_A??Oc zU(sm=hqpr4m#!GBHhvz67Uf?zIruJ4g)=L#rFm3aeIW0p4n4nv@cK7DkGgTy469n^ zv~Oz;sE7p%lG4s=BIIwi$M(X*+<%KmRNoecDc}Ynt^6wK zBJQbHwkxab*ALmeQ=W(6P3xgA_*C(nXrtw+j*Q^eS@*z5R5K0qWyDUGC@$7_U99`S z^ST?_9q@bovd6zVj0-kkDd-~ph}UJ-Hb0#Mu=ILV67T`_YOwHc z&<(Jgl}?#sI&o-e`1|dRo0+}eDmM5JxJZ~DOD@C2Xw!OY*N2UaVy*yRe#%bF`&D*1 z)MV11rJ39Wwd`!4=ExR3Ja6mC8slZ(IOyt_Wo4*Y)vUI$JnH_TTqTcuUSEuamDI43r?)*tpR9vwasUVEyD0-ggbQv&W>gdE)=uD>E$i5)n72 z{}&{in7%D^*HBi{+8W~sUa)}mP+~)uceu~tbY>95e}1@e=jx>Cqp>TN z7g{&Y1xxh1zsm?6-id6C+3oN*uktA5S-qC=RAPO#OtZdtggQ@-ctgy3QDg?fUTi$J za(42()qUxG9R`hE(~4eML7?u?VF)D>Pxdw~_46Iu8sorMkE=Nvh|7hNW#_Fi7loIg z!_K=)LU~iKCd`7w$28RgPNCDG-e0%0(0WzOx;0PSC11O{Y3qd{O-8lsva7GR0)}w# zVzhZjoO&Arhn{RS4iXz|^TV8$GhbS7I-$6#jT}2It!kbLnZDLhp1BelvhlsBSB-p} z8{Eo!#fUvG=|i)ytC&;LI)U!L8FVM|rOwduA;D|mSj2bC@s8aZ}3_mq@aC2%l(k2R&6mi=TsO#-41QDHM zZ{o?D#$|2>MJH*Ip=^w`&-~i)3X~EuCHJpT0J%uVx>A&p>1p*i=I`Q>26@2Lg3q~) zeXzmyie{~ZbYKV)F~|Pi!>QoW5Gdni+Pc7zIV7@F94ayIm271SrNVWQ0j)^-$QCsI zV7HOy@}mQF>gLBPFa9R{*}VcF@r~CmVm4`2%>yu^`~CU(FTjp8@^4{>TR`6D@z(@nHwama1(P_c3N z@fao}rn2v=kGv1m#{5jG!yC)D5Mp2obrM|f%gl_D=3*=q97ngg?GOPrcdU`ftl+1e zXZoJvqp7}hm)XejHh$nB4~IfXv6z_BGUse6vmVzwfPL2}GSC5Xe;V*s+AG|rkM(Cw zCYRX*39ZGuhbjTQX_F(YX?jfGC-`E}v$-SqSzT#A{8mepMAL69#xyauFld8nZcxb* zL|FJ%>832akg|HpNgeGFB0yb$C~>XQ3aI48*cg~WqZ%`31EG@LmYZ@a6#&FETciOTM>d^I(5vv#z3|8xNHZeH1$r%6|- zEyXcu7wzzkj**2bC5Ug*sg-@(0cK4f1^fgtAN_j@W&SCqzA`i=o>vmg| zRiEPUNNb;VggvQG*;n3gULSi)!97|e!fa=nxizd(d*IJ}oReEpoyvJrL?~-$Y3FPC=Ni&28l8H>e=qqdLiNsB7q@HRs^x84^5AOjs%u_SLJ+$Jn zC_!;@J%64@1sf-;q5CPQ#+2hpmo{+nbot$%UT;B=FYk!iRN82&R08K5zwO(PqK8B~ zjWoJyyzBWWlA)~`s^gpMAUOE|X}BGMt>3k^#kOmi3u-Y9HKbO}mh$4YRe<84m8+6| z{C_#4TbX;9R(;9;#cKxiO%JEdaviMu6$j!lbrgfmmZ`WnZ=|Z38G(%JFN2}Xil8ic zRda1LWm+AL4SvkqyoIap+blW(86rsRl)*d=B`Hg(?$0IJKe_Id1riZ4s@q?&eL$IZ zYNpf@hl7%y)pV>HU~4t$v9RANfoC2jUs1(GAI<#hvGKFgKc=!DeR}p1>%B1l|FHL# zVNrHn*suahiJ+8-fPjEV35awm(jnc5lynR=q#y{=ih#htNOyO)^w7UVE*z*E-L&_a@M|K&_iMC^NV3Yr-A!0IkwLpB>V3 zHxfN~>V#poJtiEgbI`$j>7w-8u@E_O@RBSKGEy539~H(roz}44Q(`8*ZMU}b47>hR z_`RAEqr!JeF8!@hI_tR?0Bskb&g9;(fJe}G>cLnG9@RXtJ%N77qW%y3?<@Nyq&F@5 zx3HzCa5+km#?-)b&4<*o!KK?i)&AvjU*#-sqKzwCM%rb2u^D>R!ioG z9899^Z0)oCK z)}q!0^&9)}&PTWo>u7~jl-IUQ zlcRtflzExEcI_tSI}Z^EmwN`})FR6ioG{u7?Eu_uYk$1tvdbzGVvCcb_5WA+^X@-Iwtiw>kBXg zYMiRJkV^4c*W6g9W2)_m$rFn6}MP7@NIK7C?WB5Ta+ zU2}F%*$r738aZ6QzwXbHF^4=h8F%1y8KGb|8NWd|wdlAS)Rkco_00^nwammCy!Et$ z4dU~6to#z@&I-5$4}eGgh2=BEXS+tE_Ht6)?A1;1V|NY0Uw(h10vq8Dh?(m0@ALn`OK|Mjf3EG2qaq*qoXt zsNtReuo4^cODhTZo0Y6Hnhs5r>h-7?rniimpC8e1yB#jS;C-|!X|%~efAS@aSEqwj zv$26@6QeW$fVPe&V@hb2EYt<(Q_jsYsVZHDPZHj};5JW^Z#=aw9bKWU)vV^NP|Mvl zU~+yOrl~{ALZq&oDTeYZ)--n(yv2*-)#<{W+%TrzLYbP=4(6Rs?2u)7>bQ6+YFY0A z@Z%*l#XlBN!Z-QOv_NhN8ZR}U#J|`$?Og1So3Fsfciu{lG=R_D^I90l3TC3N>V9}- z+@*88FNE=i(u*uPBIlm@5Jf&QvX8Y1V1TKS>q`uDuE zU9Z~S^Jg9ATdCgj0_;wnS#{{yl+{Osq9UF*-l1_dXRHM#kji642V_5(jLX8Dd0s%p zjo}|M?Pq@+y_z=dIS1%+B**uTY|&FGYjR$4r7HGZT{3+J2Urs_L%Wnw`=mtiv+(&6 zxV5PsS;~jXRqqLV;JipY>>- z3B7J*r@lHT=;{#fjZb-eez51-3t&-Ef;CeHEF_~Pq;FR?j#ED4Yv4>c=B_-|vwLOW zzO0ZMQ{GN??K?oe47j)skL(@gv71_!WXXlSsNh(S-FnJqDpPY70`DIxC_Ua7Q&1H> zJ_M2LA@=*A73ZGlT)UHefMlpy8C0+v^{Z8`27k-Ss!jGq$UhGh$iIFmYqs6`+*BM@Fm!QhxB??g1vgWTI{GNH>e3l2K zN5$}OzjDbDI_rq(?^%yKavxBe>G?#|?wFa{1%0pMqWbnb<)>VK>6JYHPS0hPV&vx6 z1jl*x@QsUB*T_z8e2)q{9w^Ksf@o&60y7kTjq7tXfyXCu{PK8>??0X$JgxU|D&uA4 zF&ovvbZ8b)>V z5m(N{S=3&dS_lhRIJdAkJ{O(HYrq1AJ3M;87m=!Fbk`4!_u$5_D*kUV`s)0P*|{ul zye#DX|BmyY(gT$>k@t&#uD=cJCjt%$hL??Q_WYFLQ<`Vn0h{$cxe^@}g{AJTuvM*n5T zO;CeZU6s%{L!T3VdmLQ|F$DYZHRzDKoOpcB3x75kPrjY=d98NCe0P~ZVQ|FGe=}Ji z4R-V`8Cx+lbskO$7+9#|v-CqR)cIxOV=6e7EI%7-`aYZY5-^B0V|VTo=kwYkk&RyL|QE zFY}u!Ni+TILqi)DG}K4uwfET`0wrA+UimZ@hz!ld?+!WR}Uq*Y7qy`CV_ruJs2>sP0s|`G?R< zhhJA~vyjyPTXOO1i_!Vk#kmrNTD4@QS9{Ex^}%}38Veb@HWx$?^9pJB4Ols9IU5YV zZgAckh=^n6-hS$dlRQ9%lSJA3Mau+S zxz_(G@A^$DzUd10Wxu8Jk`ZJdK8VjK5TfPlx6V$d=NnJJ_1igB+URcRa{uoJCB{Y` z#04y0I8R=Fs;{Oe?1v8R8X|_AgxyP9x%(MCq_j+vduNMj@z!sT{Oj=iIfk~jTf>3A zkslr=s!x3*@D{}U>EIAKB=Cf~*e$CW^j}8_qKD~CL$_iA>97zKVd;W=guK;0ieHcn zZqNU#{`gj5$Qeo}Z6^{#%+=04o52lmU!(utqkt^25OH2_zeC>rlZ}XpU7m6VoPvq* z-&;lWzuYNOI4@PMT)Da@^ITl*f4|oK-xmGqP5<&iVi-P7_U9K@u3Wje2!|$z{d+Np z%|z@v5+3sQLj9sw+d*_HSkIrod`a^0CWgv2@ijk^TWbpL977(rl(DXf7Y#gbGbp#oYWr*noe&W12Zmeg!ITwz9OcQltudbP6@`wHHrL5g?!Q zMthNU5|oNzJov-MJ_`rpuwrpM0!1Ewk2INojE4yH7gUn{<3-<}y=?(w-C%=xS>o_U zUBQj~r;ivrz1X~v%6AeNNV^ZQ{OO<01Ac!?coh$E>oLjyz2_f<^nc~~f3`01%kXii z6}^38t1QP}yL+4ecr}%)O!ngJKom75xZPDEHQrqShnlWdf8qOQqr8G^a{bcQy7?vf z0^f)@O84I&?}bMbW4J?ojUOh}Hrpp+Nx%Jv&;99JU+t-xOCWTRgN@0T-3Bk$Bh}h8 z4SjUqN5r{E!}_BBWaMHwILdqxk-flf$MkX;rZwf&*`Y-Ob`yE2+RGe&d}BEb5*U4Z zjH*9+w8vaZy-gr&N-{swouRZp@RXccPkDV-PZB1GVC7#7RbB4)^693uYhcke^wggP zA7Jv^e}B7eiuANfUFrhm&5GLW-=+}u&aj)wt4Vz^ipK>)J!bMikXOUBVfpx3f$4Jk zDI6E@1Ch^nE6ciF9!{0LVa>O-d+4({%_MvrJ_N!xsSOD-xsz(}f^aKcaK{YhrA)@5 zAFa>9po=&eQC0l(e$P^1{Kq$7q2;LxSIAyoRU|QT*`$iv`*5Di$Vs$1V{;kRW$uyo zEcPmDxWDm1)Do@mQ>JpOK#~Vaiod#Ed+AG&y~?zwE;u*Mu%F$5tqd&}kbUB%^YmCg zY`i#+0=B4mDKHCyUKZ7@rFfnqOT#74X99Xmegt)`Etu>PC{T{Z47R%SPxcxf>X`Fa zvJ2I_CzEiv6gQkE(rm@Z@+6qEkeJHGMymIi@q^FjF3w{l80?(JN{2E08}+AodtORW zt(ny7B$AaZo<%^M*-dWxen>r$d#9Tt$aFFv2wf_%vgF^;cA%}D>*?aEFSH}~ov?1% zrxGkRmAa3DMosakQlBzhy?y_0pH6+OhY+dmgcfjBFcjDh_8in7!+_5y+l-H5`rO4d zE}VO9*XQDFoIkv!`W>AvYg0*sWBbN#TB*9#+MX!3G78H^f#==LGke*OuD?RAyck%MJJ}`DN-pF4h)O9o`*2* zPlyTQ6h*b|v73Qy>!;~`5#1D-P{tl=eHJh5u#oPk`cLp}(-WP|<*zoAsu66Fx7+ar z1J84&-Y(>@b;{1~XH<4JiRfeXj}0x`#sIakJ=`9X$@=JCKHlFALCpV302&7e4>6DY z5K0C!0`q=VGUq=nY8^7QhaPKPpnB|mx>~f1F?NRFuv}psb6C8qY^5+g z?>(PW2h@4R-Z2TVrF!^?)ll<+)8v`Ker?fL8ww=nj1}XiazSR!sEvh2Y1?*d2xCvV zX6zuC*;$?7Xoty8lM4_)&uy_d++RBUzAvjB5H_T}c?b#2O{HLlw8rwY>JgScM`L!A z_M}=QfVB{ud`iK+d-sV?Vd;+_Afo~tU!a|6rY9zKqN0} zH5AfJ(Z{G0nv*p1#zEA|v|x!EMcCabRa+}PDFkzjeK?M0EE^j=_QAjbE!Msyp>Rxk z0Ryjz8CsQ4COHHztyZlpIe;VoOTn2uIcET+G7YshmwM(G-2gPu)Ffl#j@?*CO?~!S zQ8@jU;6cBm2#hlYi(ZcCH7;TTIy%j>^wIZsM|t|w#Ur$^-Is6MzgUjLrVLd zxPAakiFB)3;u2ceP++=_?C}wz$QSNZCo@d`LNeK&I)GlTaH+@DukK`OSRkTWoUeZ< zAepPub4RUuC1*$=VaFex=gr11>;u%)zpp!k1${>@}645Of+f$SOu+`8A9`kE=XW4 zq+jUKQNN(BT`JHH#-98trfc;jp88-^@sZ=PMdoUq+5Us%#TUCDzYz>EGW3PDfaz?s zV{+xGDY(Ya6O!{Ym_)oEe~o;W?p*qONtt;ey+jz!{Am#Vclsl3{)HfVhQVdI5FYv74Ex=V0#Q;r8|n!#gSYkHQ|tx|Q^WYze$0t?L}j z8q%mlH^0;4X&TgEfs)DI6alK!SgE~l9WeVxmAv^(nxC1O1Mu~;*p3>`_C1cix57~) z$L6VeefNtq!Yq4Mjp`vzX!3?JZD19AHf^=zgUttEG<7a-zkZdTM!pQ1+qwg-^S<`& ziX{eL75+nwvPg*Y%36t`0@mebj-AKmNwK(52rv~R{dH$@2)WT{JOP)h(e8tfD_>QW z_+_ZSL_HP1e+r(Pt*HG9!n#U-U6zD}HDC9MW1h(^^MM~vZo$)vxALhjUGZ*Ctl`q_ zSdtjVFx!|C&Cj3EXlU-RRCRCCMUhbHGDB48_O=3Pw@m&z>E3g%DsO)!5je}VtLJ^f zT-=++N1FzG9(ntvBwJgipa#Xouiobs*?DF?w0BIPOQWdzdap&>Vw{j%wTfE4XzF{+ zy2@S#%27M}wIxQsv1vcKVb{j=Z1l-{1(J~i6F*_*hcjL?%F7yS-mbdUwZ`~NlQm)6 zPU}gfgF4df^`6~bM`

Qrxj39kzTuUVnoyjKw{Fb<6J5#JsIuufCf=r5v@VlJlIC z|7=%v$xz);^vJrf4w#t7Wl(NvKvk0cu=24D#nf!HT&5rhYj;&pRx~2zt)#|nFP906 zuOj1FI^wbYM>)?n?f2@=);H|RL$bUN!lFb+>TbI})AVBl@8ohi4SA3J7Qe7nQObLE=FWJ9L;?Q0yp9J*t|o13}XjpcQv zXPEU3h*omQRxug1hiARhnuwN^Lx_!TMMH|$#xYf+JnkI)Bz$)!?86Q6eyTz&x@~u( zn>xpxclAYkYtF33OM40mnCp=?913@5t)&Kg=sWr%*Gh+?Al z?fV2eL=UlPP~jGgbZUOj(Ri#;P%}?v4y}J|(j+{TqU|*&M2noBo*O?H;AlKGqsfW2 zffeN>@q^#2p7850d?e@^3k<>6zuzx(^i`37p6StOjs94{mkke}v*YRQp|2KFhQ%Ws zP_tSOm4SwXu;_L<&9$4^iFP(lT8)R@Pc3HZO8Hsgb|nXn@PynfSj=uB@0w!T(=y9B zxu;n-`Tf;qdzi951jOdNWLf>ZgAc5$nJXg7I=>Ye?1(=cLlw&#;rX?@3o9XP^8MW7 z{+st)Q8Gs+J9Rspai>S)7ir~>q5uPX|IEn^oz9cP{n)0>Z*paCqhZdt+-l`;(1jlOBgY=bsKu0DJXWkrWnVCY)!aWOx|6~yW8=o`E)Gk1fz}n>RMCD zcoGSzMss?h46(r?E`lhXw?LmxiYl7%TdVtCMtt9rs;LnL$L`v!xIkGw+xiUO^+cR~ zoGaGo^#N%O>t{94TwL)jtB$9IuR?k%CRW1nUy?T`nq}I)+s(QnD8kA&|EW!$-D*FX z`rgXNZw96eo_%2i@=p-s1?H;J(GHn{34$k^0ST04(Ur4cCA(mT#|)1ksZr^@<>k&b zMKOj4hnQ>rI*nE(f}F2R4GvbiU1Ee|TX7J&W1_6rqDO0~B5&sTz?})4t0YDlryfm* z+kwudv~MK{MDTr>>350WcYL(|j;)Dq%V)Hcpfm>_8e*F+C=k0o28g!a8O2AXpNHnnZcG|(lbdp&9OaF2ZKq}mw3?9 z4c2crKHwoHcc?>f-wMk7njSim0Quy{g)|^XF);VHzxi?d_A+%FmTo8abj$gaM1qWQ z`+BD7`~s;?lYb|d<_BTYZbMU-o{T;Y=@3Ce9mQw6?n>I&2>$&$RM=x+SC$_0>GZbz z?i#m|Nb2w_I2-%k@4g9Iv&+uU+iIydSlq)&~v-coenbDi$h~*XdIs|negrxMeG86YF&Y6p*`0gfHt=D zm#!@%oBU1MpQd5D(wQfLX{FZdldIXk4$PY(oA%vk5|Q87!=c$IktXDw?-1$OO?M>|&-eQpKyH|C5R z86A(YDDfpq^s`hlo?VZKfA}Aa8+~-lHx{6YDRh$< z6WR+Eq4wFos1@xN;7Mu+$KG_6j_{b`@3;U~v>#e2m_-4j$%f5QzpyT2D3U`nw!Zhn z;^@o8cK*SWNq*lB2|(?1-?m%aM=2$iOV?H&E%exZ-aZ5^4Ps|X;zrpoF7i&b)91Pq zBQM4)3LIT(rM#NL2wtduA+q{3pf=N!B?ykbw%0azcF$^Af4zY!Zw6y>e$Qr_N>d@b zCU$X#R9wQj~L(~o4u&W$|8u6i9OU{vIylun-|DmNk>AY$2ufw4}eCljqw;JSGv zCE!B!u<^x=HSH{+=sC&qw?`Va?++)psGlWK{03||UZRd4OELh>9Rf5LSz*nhTvkWV z{=R3qIn_FuK!VXT)6RXr{oO4(IU?wNlKV{(#Y@qn^d8R{L)V!IHqvnmh)i&^!>K}SU43Pr>1PZt-33>%1-0u& zU)S^(T*O}u?Ho&HUMV1KuT0E{=WBz(50s*|OdDRrmY$QsL`awe=$Uj8?L_S#ZZG#;lJI}>cI=dz5T zcQ*)P73|?A5+N?aDM-x((8S4P+Sa*(x|cnE(P~k)gPff2D_iWD#_hV}&mg;IxN_+j zeDk40EIi6jqxvY^$z~WEsQt{#H&(bE-|aD(r1kV;m$*Z-5zCUZ78Bj)*YkTKbO~2u0~u;91(yV0rB}AZjoKfdC|gWniw||* zBpft3^2{$ih|o%5trzS{%uiVLB6`8`&}E<>4XfnkCxhFDD7wC`kajjM&nU4vFrPC0 zHeD#R_8flPVl`A|s%b7f>lbT(v*%UNN+wILJyy^TTXb}tW~Nx(?&6lu{#xKtir+P- zC-MW@nS91AA%Grq8~9<jxvl+8tA*n-hzE6o7#?d6rjQv`pF{2%z0yl)ia!8~ zm{S+9w3+pDs!*xk1D4e1E%5UnstgcUHCrop7U(wPE7VAN%wEl8xM9$y;H(xRsO!#E z~9Z$A-8AkV0@tXSq_X(H0%E4xf#m)--A^oY)E&qelASJz2@Z zdvD01!si|&XV=BH0x`k%vi7={IU4(&IAw{>%g9vC$&* z{=lZUy7w7^$z8bAw7jY=vPHhD-m*@%LAIE3xjirIxdIA*(VgO1S5o)1S*zZd?<8~E zjwfO7L3l;(ZF4CTBm2f$strYM8K|z)UJ0Iarg33y09v*@z6Z0r1~mhMIi@%_xEAH< zl+J1^Ew{81%73ydtd?L}tB{sYpNI*XFu_NBkuO(Ku!AljQ5&N^WkO}A4qj~226~H5 zLQUIzrx6`^71Gt-!x|Rw9y?_SE|Z6@rF?aZf1;NH}8%nTf(xn zl1cL+|2Dk7-pSfkd4i`v!!Ki*3{{F;rlITKLEH@RIYet)*76Ru!X9LO zeL?btNNs>;XD1=cUc~O(!)!-YFw=+UNMfh^BxBVMAV6Ez>p+v9*-fR~7>j(B+OXmD*&!u0{YA5G=!z~>zzFdV-&E_kXBy4@)do5%R*o1*eQD|N$)Xs!yrxSyfQn0LEfnA-rnaX+OBN7ff z21+sWPgTcV$Ntp;3@i|1l)?xBaUJ97lat=pQzJjKh0oYYb z+FSENL53hf8`jo9mu=gvblG$iM((0kb5FTEAmqc&e%A%u<6Q${?;7-IzMNHaS^?t( zz4E7es6sq)PHiku4X+648Si|M_H`m$@p|Jcy^}9z22uVQC?(B8?ChuilS)cUumpNO~Kr`@VoGAin^z(T#BOtP|=hTP5;9;W(R|)ZRTNxa`=8uo(x(aKmUtpy(VyBw z(xwCJ&H*N;`_TzbM?7~X4x(zJ-&JH={&v-2a!1kXR<)}iCdL> z^Ml|*JN@M#D3Q=QZ+%bPA=jG)0Lza8Fnz@HjwR0@(dXYwMir^aANj2Fsz1zJY;znf zu|VSwNhm0zwLg>2vt(#C7H#afQ(mn#x;e_L=fmm9clOp%)`Wt6!%X z3L~o)AM_V@@CnXD`A-3ce&A@P3u(-llF*u`Z|klF?NHa)7Zz;`mG(1_iIcJ>%1O*Z?yU?WMr{o@)8hX>MCHP#kjV83Xl z9BARLR!jBP!O7Ovwp{+P4Pw(;By7x8Yy zzdC>WO&S`{s9Z=~EOcy*jGMOPStV8CHxmZ$xMO?1R1TTmU>_kkgcb zo_uqc44Ybk#d-kw%4|;@8>CQA;8_7HD7(R+k}~}v4yVS9=WB)ayZyXgIlyR_M78R4 z^8CnKRA|UdICtgon}QWC`@mijSZR;&idmcUZw_PTvNj?u@6uuHQZPX1d4DGS9G%-H zHze#kJdjb&lu%Fwn9ZrKw0d{lCG$4yKrTALE>_=i;RW%fL6DV7S9bTgcAf~j#;IZ* zmorIDS8gjYr!b=M@1FXD?%9P16l}=_va6tpZTk4xIfU6Huk^J?aw`sl2tH`hPRC3lWh7L= z9MdgF0H2utjSbstthSJu^ngpiOxc?;^si_)@A|QVY4v0axUPaVrk$UQdVlieUmQiE z6^DZ0*t|$ez8C3d6DGZJkgz9GmE)&Q8MHA=k)?V z(-6L*u|T;SK|(Hf_|r0<)Ru>rc5N-r>`R4751Vnsyj(OB*fHAUvpas6t+u0MPDxhh z4-ym&Qq5PypWHc@>h3uHC<|#+Gw=jcKHZ4KD<(LH&v_WvbH`t~G381TS&;e-*j`NKH=c=S-*PN@IDrkfj~5{bNXPc{&3T*p`#B zATBPG5I;tri?yuFtzmTW%RJHpvyG2S*9$3zFV)b1rRZ*&+V}Hr3l{fmc zS{LZTi!O#7ZtUgP5a6O2I0h!ba%OXtc33Z``kZ$V79K70W*#7?VL zI+(0M8rqRWL}9ZOXSsVFX+^B!z(LREn%T6v!#&)0^0p$)r!d^nW7yr&1lEsnYoZMN zFGmZ|ViV@r$l*OJ>c?Nw4We$Y>4A-QThbgb6U`H1?U9Nd4X!=F4C1=uW5JgPZ}ME& z1ZRwIK4$-|@@Yx-XPJT`Kv|su)ZaaRKiy!9Zqd@Xyxv5)pvO2;RL*H0u}waX@1_O1 zVT7)NCn^kO)d571D`Vp{UZ-(Mdimc25Iq4gh4!1SgnN}!3Rn9;e3=(Emlc(_8>{BW5T%mgbzh4iv6bPc7TPBK5`P9_lQ0+ z1Yr2lu~jDM=M}Ejv_&8gp);>7(Gvl9GI<; zgo+?GTEI}c`R+BYe4#a1ULgzop7_j|CwPH_;lW7&kCHI`uvxmoYb)Iox<;aYsLz@+ zzk$Maor{=t$FTJ;ob2ZE?glMK73AZcYqG0PuIhPfigdi}e_ZqRkW}6D;vCc6RcQX6 z-x?){5E93UZ#5I@Lplh%spiyy))fV6XO>d85)vH`&jV@FcX@a>R)O>sPXFY)$nqi_Dwi!6f(xO7 ztChwr8Vjl)IK~YzsA2czWyy6s$a<5yV=abl^^#|@6i(d-If;x{GL9Evj9!kBzC^90 zR$4>^HwT$kgCxv1&7s@stgu014f2{SF%k}r`Fn{_s8`YGhd@4>EOSt_Ir!|e)>VBW`x0)`^fM1^@Q>U-fk2DcBaZ3%9+O8GV9s< ziL0y8GL_3b3u4dT3t|Nk6k|N^T3OdInBET$CwtBpc!*(y;$sM>6PtO!qL~P%uT!QvaqUSJj|o)TkoGtSd{ zx9P@hClqHrJpx78)epGtxP?)3#g%SYJ=!)6Dp}PN!xpdm@hxF`534Z)&GfI%d(KCA zvU!IytM-YVyPQ19L;TQBejH9fr(sXqde+@$0csq~Z3!A(`b5}S=8RoLs^xzz*NHs& z2|(~)0uZ9y{-CH*V7Z=;F5S^MtkrO`k|0dqtyFzzr}gr_(UGiO@*vsN>mv&ne4A^* zG2<~yj2tWE#Ey#4Cu%6Dhl@b2D~@&{3ui`u6KBaul<-QVQzMbf{pSg|QowSF*Ze0n z4LszKYPK4oCs#I013;oim^;A_t-67P!nw^tYu7-hLcrzddQchKEHs9NU3B#Y=0}I5 z%aR{O_;m|>Z*K8*AX$k9l9o(4Lch^FJk?gSLLCAp$|8o$Ec-7P&AW1VHrkU+M&$T9 ze2Z!f)1Dhvrk|X2b5%Q^7?en9AJI>|))k`Z-hc4LYxF^INc6P|K{2r;ZX5sCZXa9w z+k7uvtCp*CH^hWxo2)B+-i>je+|hmIZa-Ys+WGDP1teLKwffd1$~Tm0r%WB0wdnWap;HxxIMrV~$5PII4yVAo9XD zs32EtTB5q6Nmhu(ge9c+>O;dd<#R$R+H522*&#laC7v__mcc@)GZvGyqswGvY#@9D z@`!j-i1B!{srXGSXKX;+gORiYIFRIhH9F{7RISw$rRWQ#@AH(u+<-cqM-k!m%g z;-*{6dNLoo-+~WQy+E^w^Fa5F&*L1^i_N!V!*heOFu5$QJsaiZWIX-c8vTCSORCPg ziHe}@v6f0BV2nu;UUg4Y$}ij)UQ6)tZ-fpnfq0r9p1NIRKOcLJi4{Xxz{X<85@L2^ zo{;jD5pL}LSLi89J9A5Z#LI;Xbxnf|L9UAWJ==^VK~|2&MLbg0uW;j65;{))ZR-O& za+pE|%I96^C1H)($#dU^i_-PR zYIzgoI(MSnpxFXWx!Nz}gtIAXFJ_>+<&lG&oN)m8@_&TY{g!tL{3|W`!mZgCF8{^NUvKe&9aJfL{n*130&-hSwUPx-p&Vb}s-0s&?AIJlFBo;*g+JECF;ol1`kr`e?jtE_>r`JsY)*V`>WfO~ZaY zj1zy=SR!&^HK$~e>G%X>PRm^qSpov|`fKlO%aQotJ-Lp7At6&<9O6Hi0*>O97{rBY z3_^$9_3|EW5`lb-K+jw?@tD_RULop>P!{P!-*fGzEzl4W{%1QCvt{^!pb_o%UPhmO zbp!Ig!5aGsW^>Z;FLF%oZ!8zh4d;bYzOdZ8B4dRf8N<(b@z3&!yyN>Y7j(~ESzL)V z`PrQWGQ_RJNkBNe{P#v+4BI=I-SdGoV4PeMr9TKcG6DA<-;cu0lQZnF`#RkHA~6WS zHSzFG;~`?++)O|(DhaXh&PCO^{IlcrWfl8=VA!ZpElu1_7z|wgj*LLR9E)L@flH8IehJBncVkbvT zwyr}8bm&NFPpU0tRD0=B)irp4<49Q|{O4yj(K5cmM|8@%fW{?&LCg0cgl0kTL*}0> zg%cQwfND>t(cAG*8wc+JB=ArP%w^wyv`8@wW;%OiJ}JM7TCy5TT)!_ZU?SNh2O)n@ z`P!kUIr!Vki+^q)fXA|8G+v;b+eGnU((^(uk$}f!$XJwO?Hw)ymAB_zWcs5^pQU%c z`{Pifj^%x^%#t2Ux#6hG*Fn*{ua?NRw>HoL_P5waIoSF;(#It@;~}yeCBM-9v$Vug zF5524Dj>oJO9f-;aZ3OR%iE?qVSn%k#K4H4bn|HdTKN><$6LPr={>mu05`YvSe1{k zJ)#Bt&{`wi?GA5;)+?O{V@<(6L9alR{&{<+KVG~^YjQxkGBQ5f+E}zs8OfMO7 z?+LpF9>U=kSn{hqQZiur4=@p^j@`PFG9(!C`V=YGi>P0JSf~?)jrzZwkth)<&|CG?7V-#ch@ZsQ==Ni< zT59sSkU2-gMyBUmOqb*PxZD>olMDD*s79*1N%;-+$y?|nY(6WAUUYMNNYOZ_%}nMr zAm!gq#{-jeWI*7f@~xzu>W@Ik7~iyuTfc--k=*-6Z8_3C7CRdp9(ird)=EuZU~Wn;E< zE)HL#ufKVZ@BZ?mN=5A&ZHDBO0FFqbGr`V5DH;Y4$kKX9S7Ny>UbbnA<#m5(1%Yr4P)2<-+Gtc`>=JYh00 z8{t_VLA*Ab<$gLaqZPQhf#ACAY!h`LwvA$;0KER8sqlQ=Q9e(aYV8XY0_r0Z_Fms~ zuAvdavH`M_%hp9-KU&D$cJ#O3T}q8w8c{{9Z6xscr1u)4szCW>$!Z@$Og}m5zPpJj z$f{c{&M5CSFzX~8tHgiwoPT-Av&LH4<;H_Z08xqO(F)en6En9M=$u52yeBVJ*>^HP zTB|GR@Z{#lLOqNUcJDl%>{|ImYJ#&M~gh1x{psNgnrs zCGLn7Ph};}Z7t3{XC8C=lPqbs0HO|!yoDOuCrsRfR7FxjL&T@H16K&5LfD0r$hG@@ zqfC3y(_HC?j>c5oBt>y%Z_+U&f6PBAI>Kcfz=dr<@)VTSu8HqJRA==FwRHe z(-VCqcHCd`K7KH+W|^L!`wq%^^6kdZQ*-lq&m+v#G_mv2-5Dl*_i!|8mO1oGwxE13 z%t{S$B`GuT5f*cl8iI}b-EldKY=1$eVCLCT>>O4BYM|?hQ?HP@Ao(Zy$hiI#H`An< zXp^nf!Lign-sFn`3V!c285d9X>lTfK(e_D{8d#IvdbFT53$8){ngY%iYd1hs<&+iPthRaZFZ9 z9~#6VmN2T0gWy!$8AJUw8aLK`TW4w@Tuz$ZEOtRV&hFMAr@KKeSXU zyzkKt5HkxVH7bm20oywoVfp#iPf7RXjITv4kb5|x+mHU} zGB-4vl_8vuD_I0_N}qUT`GZJIZF8Rm79D4tj#Td?0vp-K-N!X~`P1g_{AKg^Vtr-0 z8Ce=W#gfdLh%cF*ox!Hau1M z#_t?5h1}}2E~e{U27(4fwotLhu8$Z=*wU0f{nb6Bu=&b+-jG`hxCj9ivP0rBc`okU zBSj&DF_ubu98qRX`_c7fBtHq)CodN_GSj6!?yXSzNO)3+`1JNOW{A0-1?O@oV)gdso(J1VfkVAo!_FiAZ3uUnmK{=F8!4F~wqpgF2DM*(#XQ%X^M*R_O4 z+RFhKfUg{W@_Y?P0~MGD5Vr)VhyHgn#=*x64dIS@v&NT`EI!RIu6R(-u7uaf;>d02 zQ3!%RY@RJ7WVuznpT4Uei-CaV54WV12$Hr25a#bWgF$+;LMoLS*Q)E(Zh6^UoSzhK z9-9;bg~>UYKD}0j>U&lQV7(L2zqF zi@wd>)m={hO`y1zjs|(GN3Gg0@9J4J{ej%>>Zx6}bLwXeaQ$J*X-+G1VLW6{7rZvQ z#br0R?i9FKhv{CSL3|}xKE7B@*Qoip_}RvF$8T#)Js0N(5!Rmj>Tz6F+9SmuxIPF> z7+)h3BG{Ns6AWNi*+V=gH9yES>=fMgeh^XM{o;J$z`Q2)20}O|v?IIZMb+ASmgR3K zO=(

~mlyQTq>WFSx;&sljW>z3$~SopdWF9bH3rIcjz18IJGXo745__dI$Bb3#Qv z0wepeq;my1i(|4{{hDQU+8gn%LS(MjdX31jTSTwA=^_um=_7y8$Wb?G;U~F~>VBa` zwWISu=$==Iqhx{+Hs~yikT7?@-O5j4gi}7!xn&1zI$T=V72E+Wjf{30v}v zP3I7Ufhao+qBFFi!X_{eY?*6V zVQkx2>`Aew$C8c)9a}K1d0YsqL+wS+&!wE|JB~c&K!J*c2T!^?S-yG>Ool$qI{5lz zhkLMMk`2--TN)l8bP_z&?j$?~1Wzb{(CfjJgZ}9DD`gujv7OvBgWnW7Xi2ECP0q{F zWM*0}td>{09@r%^$f%eEe7%E&NV9!CwNkHDM846Hv-{?C@z!!$!A-ZE$uQ?BP4egU zx3=l9fTI01gz*2v-g`zxwRCHvf+$g~r>af8BA%xa0ic54~!wnl)>Nr=B@$)?uep+}}0Y zsIbLgRHRyP)^%rer+LeY5d_XL4Y;7sE4(~00{3j@ze*kWv;`c_8U_JPrI@T%>~#dU z^qc%p)z#>4u{xSdI>HwRoyWZEhb-BM-!$KMxY)tbPR&K(P`oU-@t*#DUTyI`+tNkf zE0y>`6ph%BU}+!z-u|W|?Z~cJG>4-Wk1%pNcWt1oHF?<>KG3_R+eR2(UArz+W*#?V zex1Xq=WY6WUSrAov`SpRkIK%QK0=L#Xe)cMXmHojKzPtINAzAX%Xw2LPaO-arZ|7r z&r!`Y)B$tVytakp1R7Mwj5WlSHIaMQRb#&;C!2~FUdH`esMU>fbG$SzfmK7L*73S9 zJP?506_TW<8TH&eb<>lbjSJy32PkSmM@6elCts zn8@(Nn9(2t((`!eqhS}qf*N*SQQc6QSBVJA)*3)kvvkf>fE-WxDf2Vx3??=Z4OR58t4qOscNH+E zPe=XX8714dK3=*c>}1K^hj+N{xs8Zz555;i$)xP$%j%(e!PZa4Yt@bT=GNTqgspUr zKQ>&?1tDOmpFf1pR$xkKP_S!}v3-KKe8|!&sJ+e&+TJd#K8z$AXfV4tD0Rpr)o<$X zIk69l$vS&W8v~1q86S}m*-l<7pot@QqHhZ=<@mD1)bcv8P^4$Rxra@%f&aJ{;w^^t zdPl}@xH)=D=AO(-bB80B&6w$J1|bcUsEw z7N~sBZ*S4sxk|T3@*Fawp%89`j6iV;`haNFNQO7=cOsu{O43Bgfz8arq=lp9;(X?&ppWc3wr(fkb4EDVHlm~uCY{Q zt&CQUTJ7~_Q2nR)bdfDwpGlzh(73{?&2wum!Ti@K{N;;<7P8D}c2B}Pl5Ar=Cq=Jn zqZOS$`fyTnKf9Z2OiVvMWizeL#iVQ;I?u7zuU9xV?;E=i^n=Lb(;@LpzVgnlJJ-sU zI!?_-0H3{`d-&Bfc3ivWsL?#vxRsN#n*8J9099Ha)Kd{a_jsS_nrG^t6SL+=?lMpo zD2bK%c`VkTu%1NH?954BKIAWmkG?wGYaXpu^evO=R^SWwaIQ%^CS?H32?caNyG1P` zUT+w~NlC!kRbPZe)JBU+8q3{o-ZDiY``19#;Pu6}@2K>lKxQqk+I(BDZ}9<8Fj2%8 zy2VOABH7U4IwgCD*gk!iq6Ubu>vh>JnV8b%-hmH=W0(vh7cU;`R=8D5Ocq_JO73ls zX+(=0y`t&8#0Gt)DJ@%2LcbCOJvpj59&vVv@sKc+(++R?od7q=*{!XYY`?WgRx41D z6IE1hi;Z&ZSV-PL-HVP=58ZJcrQg4D(c4+$2C+oPpax%3JAI6%Zf!d=!}nCk*-sJ@ zeDQqI9*M$h5g&2aRDC$$cIQFJQaT00j%fCWhB*LxjCKAaWwrregq7r)AOyA<;pilP z1?SrBiFhA*v~)#|NxHXB$Jz8(-D7NC2~x$jw@sIiByC5Hl-qF+DF^G6wKI$!zF0x` z8xb`lDRtelc+aMIwe{j9lI~DQa33-lVzQqYfUqMhsLdq^Xmy)ka~y*%zZ_5Q z;r;qkOyTo_>X@Qoq!&lj-UcYg(!UQVU5#~aq$%xXo5HvoJ0)>aGdM%TH9mi?MK&F2G38+eg)RhCtPe42Vy`2JOJ z;!dDrsE84rra=O3*HKkMO->)>mP7YP%NKN`ujGW3t3d)rv6-%&Gy#dap#Y2a)81U= zY6yq5HZcZeC&dZzw%b*T-;p#nHs<%GUOk?6Q9~BT$-OLK2%aZ72R2q&p(oOO?O^}P01qhKa#s9R`2_tB3n_i*#b`R_|}ZdnUXj@a`p)P<1eEaH@8pw?Y9*_z>tCU{Ud z=JGZHbp` z?{pAic73Alj}g=PkaBAoo6ggXq20%;;1L^rR|O*V86CW@2|QzDf$~Ag>*DJLX%~FG zj7S*pA4d6nMS*wbk%mNlu@eU5S8b2s>s(>xmlw_KjtZfg!koNMjQ)>OUzfqVy8ngLxm@Y ziHFP;c2>t-#}$Do40sBfYmV2d_R6Td^h=}-dOEHeRHJN{_|?iO2-nA`8x&P2tbr0T zcQ*xX5Q0%--@J{RA9nsshwSY~lhzOxX%QTT{v4IBhqk@CgQc@7g9zCs#4ql|q|T|p z{nzFP-y5$0$z&3AHA^#@^))DRrJK~u;uB~I(vT6yMv7*2SB1V>egWUE+lvwFlGip0 z(v=MuU}us`BeT&IS5%7BG@k^)bZhUG?^#W)P|&Q^E}~xJPdqekd>4S9&Tu(etT|HQ z7fnd^i{h)|i#7>rYaLqys_4!`=4rYRv-GL`DrKm{p1mX;e0|}0N8g(@30zPb?t`RO z6=ww?3&d*1&9ttqd1&C`c@?ne(T%ghdjTe*mKbF?!~yEP85NO>w-Ublyn|GZmeTe`;Mze*|EEugd)2XPC!}$1y{LNhFcfEUpC& zz8~`TdXMaBw-+kmq|(?3-TumXm(<|6r*+qK9wwP>NB7#t$(p2}jNV zb>E_~akFJy@+nb6b2d}|CK8Drk?2e$$%=BB9n06o-#s5>n=dzw@g%09Vg4fS(x_6g zx+K1FZnc|Xo+k{wa)#f@{ix@)M#BLCFgz*OFKxTjFP`smM58<@3>PmfZ)ZSV#ROr_ z5BJ2FDi-&~JHwtn1{H*FDH&;go4j|dF!T*WtI2I(R`!zuil zR(fG~zg}khUX27aiDq5KVYHT}k8faNp&~Y>7BA?X)8@7%bjnydyV#_08r^W^!}yOE zA$ozsu82{fDqby$-eR;+zrmqq9wC)tw_y7vo?Nm5GiRN`RMD3GLywYP26PCYnT{{% zVlVmu9INz>=G_;^&e)9vbGft^vV?gg+qVf1vD3+J>IXO*buG%y%sqH6dc;JfbxWTI*dC89Fp9UD$IT-*qm3ur z{qkeX44E_g$;Y%gltv_*u3~?z5w2sld%^i5YVHb3J?hXCd?i!#p{4mWABGnxQS zbfcb!pX|k4`n2yagipBGx?4ebj|dSS3fbra{Ua#y4+a1BMo3;XOeDtDOw=Gc9-}bu z7Dk7NT+oa^0DuM#JdUlpIWa61``5uwu;c3cQB6(&LDl_rni5K+LUy2vIrXf9c_WAl z&-jOD;L+*^`4r^T-nZZ`38sQiz!fv1!&JMi!>TC%9Ovp|{l^1*WbB$WlX?DjHBawD z3~XD$XD=`OX1RtBi1gI8ps=L(>zWrzkG#uV+5=7VMs&51x$&$M?Jk*yt!lG$73J^j z8}TX-aRg+dG*x20=Tf`bWZSlf16}Q0UT3=tLiVAU$W z2UOUe$L7IR3yDFW9(vN&oXL-7 z!rUwUm9*00tK=OdjWtQ5n5Zq9l-lD=5mF&pe|1MZoO<-;FhQv~IL1qJm#HnAnfWLf zV96(xM;SzHdIX;IA}ee~*bT%ZV=S>;;i1k2w7Lc&Uz>u6L`$_GWsAcrb60iM-G=cY zXPHGhoQX}3L43r_wpm}~4ubAMq@s~{D;xpM;;M9TW-jSjGp4&=b-8^;6(6lnx%3*r z`b_#CZNdV=lBwtc?x%@@Tf|b9K7GN#imRRByPwFCQ5aP%6{mMctnUTP;4x*M!n^sa zl_np8axSf9YRl6*jjI**5fYe!hB$o*&Ek@GU>tQq0o|ej9710urYyj&B9v+(IVZi) zx5aVzeUMtAZ;MqwU3h9wx&=?r{)sQmNtpV%D+#{77(zmUYdVVWv_2<7WLZ2(|CKX^ zfbJ!{l;7*Je4EQ|HqU|+uit9UJSu1YP-KX>X~BIry2!uNTrMg{=ssYay*cmRL|wci zaM|aJf)!h_mr9TJ=k-5S6Z@_dP(x6~F?Q#4gMk{D40a<*=CHlC(50)$n>8|`$gRp2M!0DKw@SS6<&DQk4Xj{Ra zves)^e$=J#!Xo4=6Ya%#XXfjaTdi}H`c5r6ZtcRDwUk1X5+0?%`o4xL|CYjPC8|qP zFN^nY9xb+6!u#ff&{Oi*yMtaGbHO!2V+u?Ib>ILrI$-mcQ^ z0pg!SO&D_btM1r$;gUv9d=!r3a24BliHdmIWLX3W%` ztb$HBkI7bQ+F#8^o<_8;d*ampyCRv4?kM@rRO$^z4FeXrh5&lo2s4yzy<~G zTlRP;R}Vil5nezLQXrH|QhrCuN~LX_dcUnNlrfitsky%5YxNmc$g_4mE!7~qidG?G zP4kOR&L2mAvhNL6dN?R)_=0$OZkty5d&%IB;qEWHuCeGbwi1)pKWr%t{X%2w4^e5K z)tXGCj&y}w6bz2%sPGKdVA8U>V}~KROg;5ryP0!RBAqB1Adgnwlw$*?qa=Dad!lg~Gu9E?se8qLfkTgbTj`WTv++*^tB5>p9E$5kWFH&8z_U!W?GV z39Hs#4Brf4g4yqwYG(4k-_*^yC}?C+;5+O9z6dA-g!m5iGDGUULtuwHL^KI68ENiA zPUFMC(TL%cU)r3l#Rbq$_9GZQ4XfL$M- z)7)mLC^ruTIE_L3Gzu>hQIumqojs zE1WE_yG-r$!vkuytwLpgXdINgLm2k6=S_J_{YUqw;Hw#^7v8H`!XNjcQN9LwcCg3n zvrJ4oBRtb^%7Aeh>hw>@NoAldh32ZxA#CWHkop$_^Z?(7dX=!EUn~wK>)Nn)!aXcn zrh3b=tj0y+5F`;L#~uAdS~g5g3_ugDsW zhLCbdcqU7gjkZshgFgoCgRD;mMEe9c29A^*x|{6eMQrTQlr&@|ipk9!vRxwaI}odm zBt__m7M@GpD}{BoJ$!9Bz)z6=@>MX{whSHE_{H@O+i9!`w-#js(`#+2{6KF>-iBZ6E_=ZrRW#|+r;zy9{&C(iO*spsL` z!t~s(Fj3RdClJKXbmhxMI~4|5D5HtsvRGV;P zEyv#n`@C`6d$I%4{)+P_;`zib7j(P#7J3K5tGZ0x+i`M7ZZE}8;MljHivw#HsBmw8 zI28Y8ncCD~Sb4;EgFt*)5JnsXlvuxPwB&LBbV8L!yW)m!A(~x)fimpfgQ`JAMuQB2 z0wOcSMT3ek=>;-HowCL9VbQ!h+aH}?2o$_mb*-Du40kgVP!OELJdRf6 z4JE6f4N`a)vD8#sPhED|2j~<@C+92$08T z!JS%f;aQ1WhRamLqkWk^#0+iT3QZ?BXW(>v#C!cuTL<(QNDc`Yd38*5h!3s9iO>#l z;WANN<{6z?3S-+Dd!W7+v26SEr=z;}OhbKb$&3^gLWEK-Cc@SaXy$o;fqeB zJtZU>f0(P0RPd!10Png81`PGkgt2|n<8Yn$2E)6@h8%Dph60AG05DuLWJ*Sug@uB; zt2k-Sw#|ayv%RQeRH_f|csRU8@I=36TBgOJ;b(5j+Y*q?R>OfOCqH|}%Z!|S-AndK zgNugCJkz}}dfO$!Fqz29Mn=Vtf1z>To4rKzn-(AWJ6k^k^6;Buw3^w6r+)I~Eb2l0 zbefEHEK(Cx#uqoZJaRXRwL-`IvBQ4)*i5FNj|5b4W+_Dz^mRU**e(TfiSfzBeCWOQ znj!xRd{r5_RXk(5!II(Ig0i-B^ZY2BzRlBW_S__I7pY8J4I*T_`{e!PPhU;xE}h0? zO(Wz?s}E)lNS$(^wkDGzsT0yJB=9OVui>)W{qli9_Ph%-V@57bX92ZS3qzZ8TT^$@ zn&ld@2PfKP_A{V~@Xny#l+y4tXKF}xC&WkfbVf;88> zQ~@ZCINv6RthbwB@mRe>UXeHz{d3#pn)~5;hw<(U^<$l0R3&QWCp5Ja4ssTTPV{nO zXTh2`rjD_BhFd~KW8ZDD+lwwgD?`~CdBFj2)0m19&=jqGhD0y5Q^~R;SOfp%Q@Kk1 z$WOk~@8{LYwGu;itL1y@7acF5#=O5>McfDjnqa#WK|msp8zaJZad)JNtQ9Q=(IzPoj-@ zr|(Qb&*+jFePlBVxlD&mwwbk_)v$E>AACGXQUV&OBKoN=ix-tQC0z^0iB@ z-sBrx*kxCynHnRJ*fAWH7Mfj&K?imy7lW0GDY!8WUbXU}s83URLhrpG25uo!y=*^x zh26d@s&1#|8!42Iw>;+p3OtU=ws+)2L=dL&4=|^w?^-)M+0)`7rw3K_Nox-J@ zFm9)=$^F>G^TA|v96KZ(t4o@jPC*mW{k*sr&$Y&q-j%Q9TkbRs%bqc^FTrUv34bqjEs;5YvY&frpK+FIL-nnm7o z4Pj#4cz`DD3$>zT%TD{m_M1jK1!gJT#EN{nPy=@CBV)FXBd;Y~MvWemvj&kvtF_mr|D>azKi%}W3;Ve85vUis zXKa^ilwx=ZpI14{7>g{RAyE8nbA^cbcI120iBWrK>^y@NV;9S$_`Zaiw)<3pLy1Ca z&7fiW&jDUNSwyyLlCh;#c3V~+p$rs&s=EPo>%1JS3}!lM&<$b6EQZkP^1(>RJ3nRs zXwC2Ls~Qfy@@B`F3QS*^3#G?>VwbpdrfNr?PhN&|f(H7gLI4av8 zUN+`b%vYyWhuqtsPqFAHM4EgxLwBjpS4TW9mbqo;CQr93_j9M03+pFPXGzLlW~dc* zOL7T}Hh2Lb%>w|n@_Es@>)b)<`4k$d_i_t}~VjQIN zRK)0fY>JhbWV@sX@e(S@5#VW03fVmmz=`h7Akl>h_T>0{Kh;{BU9)Pml1`6=npCIb z=;c!X@}HD;-e}5QCzjGx__zn`GPp&0PyWZufhA~D_^;S&4wk%prlC>(iD#!hoc6SMbS<*|GvzSas^ z%x`F5Q^J$?v2)n^!;7_#LJonMaFuX(Q|GLp)|JjjE*q*`oE5G?j#@M5a#wwQfJfH_FEtrwfDKU<2m;{EPpePBU}UWbwfmmv-!S&a5c7SYCx z96b(WoAb||J29`@@u6`?Yj5605C)$7c1Pl?#ilM{AKR3SvX_m?`q2Nh^FCC3s&!%X zL+xwv{hz)`3+7i34hb@EpPmNMV;b6AjAPf33ronMQ5H|ikEFobRWJ?MG!Z2gK#_e( zG}|4aPBT$|D+C9k?!m}4yuL(HaDn^Ows5Izd)Tod4rTgkLa5nx|au}!pXd5i9t zWI>!7YjZ?pl&ffzcx!0O8~;3?r%98=Z8Z#b%n)EFi(%hZgtUb`pRYSppE>Ff;5h2A zd#ZOF6+u>Ow?m%+I%FvZ+bZV-DXezZZ*gQ^az34RnSFQvg!%=^sNdU&Jy${3E)(h$ z0D9!8Y!AM5!Qa}7AbYBIAZDpq`#HmV(o&>sX`!prj@_lyN3-5RHS-m-o1)mADRx96 z6{VV#~m)Q5BTdn@=ejjS1F#0#pugY;25tH!?a4O#&|+q{(%V0;D=oR$eGa4tG*uv z@M^wvcUvX)_mO{#NG%}kqO1TNhD0Z{ywQ>El>-<+ulH?TWffO!@Zmyz#{FwimQ{wq zBJTm-!}(cq+HIKhV6p+GW<5}-VwrC5_1sA1xhV)H`@4Of#}9<#6M%#KI-kOKuE&m7 zO2-KXZe1hdS2Z{thu=XvB zSi0BB6o9|Bu)r*hg?A88vEf6Vlw9UDZ>I_7%SgWg5zUJ9K4{bfI_8_rnin(j-poM- z<(%r=BtPhFkmv(k9UNL%GH5yc@}PPf=0onZpVmF^(P& znG8jNeq&OT|KjiiUF$#50ipTyJ0}&O&o7YhC(NdgN2Yczj`vFOpBP$dvo77Sk3Pzb zBHGZzSW%qi>^$kM2*>uFJviGUk32PN6bKVMq#gCYv&6hc*sh7$Fc#8wQFC6vm-Crq zxIgJBXMB%S%(k)g$e< zFfv86kFA0QGK)>U11Xouid>4kbUWjXk7j1d2sI551BgaX3ydkB@(60Q#a>Uo@vA6l z4gf>Cv&%b#w`Qx92c0AeMh>eD!fP7|%dG1*IuO-#;6|~XBkAVLY*Ks`A^;FzqAM>9 zm{4G6xCPHDlzV$xPwv0Q$=;^m*Uii>IFI2KsceixM{L*B5jCa%b>5(kh1sPyIBdS! z**Cum=f#6A_tsSd+^esog3H+y?Q`for}=GN*kWO6S(;~+UN7q-CmcotCjy=+@<>> z_{pkw^KAcSem3<9k6%n6JaGc~CC-sLu0NBQ`B(9~!)Njjc+uAv2rhSxdo;9y?iH)w zyLLLc*q!Y@B)KEsQe*wvyJWHC`FZ8nsWSb{S#&1UlT+rRyDJpVhGJ%J^TuMo@*qqp z4jqh9i^pBbfK0Gm2**5{d;RJDvn9=@v{eR$6ID*WgKctyn)x5POdvxGeSBFZC(O2v zSqXu-Wb-E0PFQ&k>HT>cOyY3)AJu|(-kogPsKMv#mZL7*(9LhtJ z8rnphzHhYk7?ples>WxXDJFyqL-%V_E6eS^UTD(!dg+h@uvg67rI!#$7W$?r*g&;$Lc#}~eyN3lxr0BKIfP(Wr(A4^& z5{-2JJXAOP>7m2tgqG@Zw|TUlJNK;0_c66}JRTTU0zTGAOJKfyO?cgh&}Rd^*5f8M zF6NZ5U)etA>T*wA)mU@Cd<}gBQc>oq>VBAjKQ~7}%P-%!?-v`i{7Z56IOYw-?%4)I zW^UK(@-`ie`E!K?PZ5E{hMH%m=h87#0;?wLt0ZD`D~)}Tmtp~;oROH7GAWgWjA3&h zTR*kPWeiwjDn~LtD%HW60X-AB&%N^~o}IC)q1FlO1e-~3Pb23Ws5|E9>SfK)dvmUs zEm^$TyXlU1o|2IXs_)vd-*uUNczkAagnj6=^3yijle>y0w_z#i-X@3Ii>H_(#jd*x zxykQUks=Hyy%2Vrd=Vs%(bG8M$6R~I9gtd}gdhVu(-piJ;!eE-Rhi4S7|0*|NLz3j zuD;&a!~$dQkl0V-D=3tIQYXsFpvR$wG#M|D%itQh0Qhzz3@3AARhehPdpjZ>_U?fr z@^0%o?q^R%^Q6heAK}6wqQ->k+TmWmtKp2LPhZ`nM3JbBwtCXbmct^MS8PC9w9<_S zFjL~07dCqVqK0{aONUQw8-hsV;8f2(l)E=hDwJ&B8g3dyzwd?g zjLP;bt{y)H8p_kN0as0B=~&)u;yjfF+7lBZd2(0fp$G-NR!M@cuN3sPH>juS5GBeZ#{ zAs0a`0lMtU0&$=|-woF!ZM-V)D0GC!@|Ebp1T^h9&=qGTXTQ$hB@ut-+gMc(2#`=) zlP?zuWOsRTWog)5TM~ii%}zmO(mp@BkX|<+nSb}bm53S(S@SrgJQ5C?6MB%q`rX!Q zOQKPeq%w+p>;7+Zy{q-HyC=>3>7_O;wWaUPG*Ndd{Ypx{xUf}t^q}cjwi;_xITJ0L zohQqAaeZp{$5(8H~RqL6)ZE7vW3Dwr1kv%vmL zfeqnFquuY5G|+eJK=t0eyYD{ry$#r7rh?!E^%`Z%2WJg+CosL9<8#h@yr^S8u9P+- zqHERH?Rd@%O%(9ZIjL41Sa-pg)33byI`^5BmBL!X1lEM``H+> z2(@$T15fF@H}$1v;ZY3@M&(bp0!k9RB3^694{Hw2O74Nd_5!LCZn8qy0*o#fLe!9*!OV>A! zbNQyh;wZ5M;AB?E9_} z_R;?Ao8oLP5l-}kYXoA6`O!~F5BvJzk5y3fm*F}RNmMv^wp9wa8>@re;h>&{_3 zujOP*`==CG^tb%)Dz|0-5Hk4b=7J#ISq;6pn`}yHg=w?|^{!;%%AXq=iC`C_)Q4+;tbZ};vK&hWf zTbFtM6AFvhramK;ES|7+L3VwVrXIRGp3}DIgECGDxrg(|D7Ub-qHlG$TkeoR-ygCm zFN6_Q;$!xc9C)~p1!J>sk)N-258?t8j?2*gSjbr^W1gkP; zAUYESSuhIx0{(bg&bpnLUM0+LGQ-0Amu+y)o$XksYWxI>ZQfRc9$CLX=Sa<-V=YeiT%@G$CCH1!v(=h5w;F|HG*M9~u0>dXLP2;Vy`w z?+uy$HpBmTfeITIG4yR`Bsl96U5EQKo5unFp1@yk{ZmVxd@uvx@m;SRl)2lr57k0ejsq#TX z&-q-HhTuQIc6%;tnVHERKOVh}i9ry+z7I{}LRv1@(T@Ukn;&W)HmzI>%)Vjp_GgYr zJzLi=@ZfrB`%k;z78WV#DY{H+-M7kj=p#Y3gZ(cRape^4d082uVQ@BEkfSG86xEP< z8hfSFd^K|^-hbO2e_BdDH_R&E>-Gy#xLr)3oqaTT&%dueyeKKd0x$D`14=%nm3eJW zVNofh0S`W~r~Svh=7LE+x!!70Z#nX^T!@R1zr)?WscE`C*zx5@@bb68CIshFw>jBA zJ?Db|5tezsKH&2)RqXiZN~7h-L?;=)dV*CSB^~@>mWE+DeM6cP`uWupe&w0#eqLuIMERL za8~j_??>nd+qL_{mjdfAQAAaLd6@q`B>iLJB6rV@I>wNs^>(FLK5ed;p~qn#Y_I7v z%&qc#@Zit9zFF!0@c&>dd?-f7>sWggLpdcmDDY1*yyYJ&r9L9M{fcu$XR~So!QJby zASEEpU!NtFBt+|3D*wSETZ}wu!Y(IJr){-W3ocCu4##ICQyXO-5Bn|s;~EyYU?z6B)u}t@!pc6zKV-}> z-ajtImhQh5O9PP@pW!|4}v6Sp4f{*iqqtB3KO0 zchu{z*;4V$$1PF*lbXMJW3Wfox4H*JiZb7papCqu{>J~jCiH&bEX&(CaHqR}MSA~( zCJ+N5g@UM*P`=DR8f+la1R@l37)FOM4dy?68Xy(fTgzf&gv8KI;{VGx(E~0&)x{62 z6#92L{)d&gg+T%+boK2ZM-i-lS~NiQ!hnF}I}Lrt7{mWCufIl@0^%Fq-CN5Ugc$$% z1%HzWa}7idzsDJ~XK_Uv1gkI^))PGI)U){*cRYS=M?b09+diNd@* zA_W4H$zhc&?I6eS^<@3=nw~odi`4|bh2{^wT?p0($ba<89`b-YdT;6&0baB6E0Ho({Z+x58P4gK~TdKGTVj__y?@1r${c+@p+7-pn6kiG9^jLA_D4tpUT9QDO z`H(Wx2UGDZ|u$h zSE~R@;^0+<*0|LLXXb1wuV@yr4r2KF(lpJk&bmaem73G#-K>Dz39`3XYkdxZzPs&| zAX7h@Ak8~?t~a%)c^b9 zllcy#QkYjGxUoS%z+$E&l1lg&d-L&U^~&P#ZT0$B{kMxsD?hssU1gKsqsur7 zBL9Zg!T`DC@YRr8Ze6Y_MAS?Xa0$9iN13%P#Vt!vtw|e9WkPt2sf{Q2EOZlWC24UsNJDj`gj^pxTtg+Vz06>5iPRr{iDhLiLO2V|FuAUXNW6~(?|mvnj_mi7 z$W5lLkv^&juDv@{-^9Szt4<6avkRV0nD zl{LLcbffb+{xRcT#?)Dj184Y@LG=N-Ye*<+vUqU86eDijpfA+sb#hX>T;VVuh6mU8 zMPvfEek(uwWIg-KVSpX<`>tu<6N(}S{7{;)r!Gk^62of1OBL950z;D@#hXTdS@)z| z1y;^;2eWKj$dq!P>wwL`CvVY2Tt)t&FsaJZm54M;E(weEaIy5OQ)7)x0!~F=(@HIy zk(UAOB@|TqS!C^V&oUN8T*78-w}%4I3l>G*Lot%w+Lv|}Ga+Ux98*DF)dIaAIa~=* z1BOhNi=VsCuGQj^YKBhkghf9@*)r#1%@uB zkOQ^_Uw#J~I+l&&y-wJ0e%Em=cH4M0akV|Kqn-`THautj_+8_#y=uf@!Vl$oT^|H- z9cXUCEWJbk*N9Jc`6BZ}e#%P)4;9830;NfdKQrO8D*kr4U!-ZWSsEm&sYEIW(^9^qlk)In;H*-kR4|?VIG* zd4qwC?)Y$3%z~kYu4(tu=siWD7*4#xmm#$|5`YTCBD+Iz-6XKS?|f27es z<~!!RjJpRv0beT`;fYbU-{CH^r(H8@ScmgpouGfv%}0JAhLS;2UR9?NL#YIl7AJcZ zS9_v5i!z-)y7Ibj#V`B7qJ);chUZ5+&UH8MQ5b_)eqFB` zMrhX-6|+!#rp&k$DGqsT3-Cs8K?=@*re%X@UdiXEIaGQ5Whpj3g*wGB;LO5iysGXg zAk3_>G~yS1(9Q0sW}>0M_q4+f{JDeCA{SC)`=4zMTZ#l0Ip>gq0&@m5j?z;~W?zFT z2nb^u4h0-hc~5;3z@G6@lY$&m+M?5~gGy*u!;DNM7X{}95?wqk_FF^VRKgICl=NRqghhclJy0q zr)&bNh8xG5j$qpAqiAekRceR}VL|v@Ef*oe#|2{n^WMn0KBpaUaU70Ew>r~MymG5W z(nkk--h(4rvnoYF?g-88NlCMz=kattqZwqYZ(eG&vDAt)T6`Fqw6dbmD+mWz>H0&B zyb|2Q&8LO+K7+f$*a2#;$tMyltf4xj_0Q`a&RdUWd^&hSVaTelAW6j`AgZ!!xn5>L zKpT07evZ^qn0#+?YMv}X@=)2;WtFt54>m#ey^-Bh>m1g?-eWe=7GN0sE8op($E9pmo=_T3XT9l_(#a;dSB(@NJOvX0Al0$$5r zJ40kc=i8+qfmxHlY4#^!&buR1a{`C{(}*{o11Ss@_=*i!cG`zW8)U zB<2h5@Dn0)(6HOJjHB8t?@;aHB{8)!W5(OxsZQvh-~(y=AA8ks65*Fw+Pwqh*JC39V*j?v;H-1dT}2A6wX@Q~jAhu=Od@a>kIT;j(l6LcFTvqC#i zUoKJHf#O zUpZVXOfNh2GaB@J6Ne=!bnmO(er7d`$0hDutHr=OovZbH+qNM;o6i~@Vtt+}?ow1R zMYki>+cUe9?Yyvfhz{GYUCgFg>5Z{1x>s;@|H7r@$_tj-Ja|yV**_DqP;pxvyLQt6 zJKRjZ>K@YDcg~)~DJlZ3M-$dPg1GYj_M>(xE8PTpKIxO0w&^wCxcIeqoxpB$u2rj5 zxJ=Xc>X++5b1I^*#ycc4YD!v3NH_W4cN2NY>BG=F84Bd6!+bsNUdT2nGZp`ZL;QC; z#d6c}wvrde5}kv)a^73UKJ_yp{}p=vn?mJc2FNCp2+n;j^@~$$Q6UpHtv;||N`2!g z-78QNpfPS#If3Ds#&FxiG`zc#;A(AZaga2EE9pt;)^v#Wo_2S`DIQx2-AF^B=65fX zzHR)G#p{FI854k%Qy&X%S$|ZN_5&3KRA6X**@GJhq5~nC9uI@`Nj#{^ zZ<@5KcXV41e_ETyUlLqT_c*QpTePIH6g<%d-W*+>G^l?zay!D#yxyxu_qZm$=IH;h z_n%QscU$`?d|MC|DT08~RS=~sRS*yqkuFG)5)hQ$yL4%SARy9vlis9$-}hEg73Px+!LsUiY>9$D2d4U(_-uojpx(Tq9#_c*V^$c_CWk57h(?I@dP6zMP$RzoVe<^#j2Wv0&*i?eSi4Tif%C!mD4;b%pp zVCkNI4e2aZ>tOB08v>Ql(reZ;Lx_oKKM0N@Dgm}63ghV*>8k1j#T z1EL^b`b^?uYWujQ9KltHUT0 zG5!(WVFWb^E1}kkr?^GHzi7w{a=8rzWUDbxKEzp9FPzucSGu1ijD(gQ=3ge=83Qs5 z7}a!bzcRPL>D1j}sG#?J<1O0Mw%7tkuIn(AA$wm?H`PC0bV*Jimko>o!Vdb05GNV@ z{6mXY_ta3bY0Qwv+JH6Y6CvO3NIt3$3G-olBp%%?Ok>W)5Y03X-)ia5-LCaosj^g^ zp#pXIea>>62|brl^oTHcnj%~#T8N2$?r`(i*v;X-ac@(rA|}w3E%lM?0mOL`(pl1X zOf4=R4efX-q4yOPN73YdjgMW*eS;P3KfLSP-dG0W`o{NO!I$-?cir1|A)# z8H6%zt6;Z=s~>Xz)x@8g@*wUPc*z)~QZNH@|u*0sj(2es6vumwCMJA-Bgv;qsrDAd5$}yO%7Y zB`RvX*6@0>)^s4nr-@irNfR3I_gJg)z#s>ZdZ1eDI}@^9{l` z)1Rg~#tv=Lz{ckjP5f`U_9`i+pzG)P?#E@ab1OXGrruhJX8sXj3uj6mz4Oyk&3m#( z%fBI`fzpBz+(tKkS}?R&q5+xj>iY$F9OF>AGS+HcuUrn_saLu+KpN$w%3q zBe))_-*=qvA=eHy>H?ElEnocA*5M#>;Zls7Qpw3N1Grj!Zn&u%49V*S!xTWUrqa?x5W{TZV&YXJ_V_G5{^t4 z-^ONAH!@r)r&^o^jb{1LASHXcMjdNGD7@2ARiU1>tY(rv@?eYo_&mI3&0T7J#p2cd zm}k*X#-XZU;#ws2r|q&8K@&W90Y}u@wC_eYh=}XUO^3=y2CvbgK zow|Dqm3D&lO-CAr0`d zrX=-iPQcwqXt>k+3^a4`)gH03WLG@6-|*tWa)I)2r90`i!~NJYK^Ml_BFkAbPf5Yu z7rITbtGGJxc`A^&hl->@x<$L#e-wsem5k3UF?%p)1 zz(v$QXL`@0!_gL!#;V_fds6xOW}PDDoJASva((ivFJQ(bN42_~TB2mdm*ID$+-w_R zlzqKk!nsWQhWRnon^GdGskX60fWDPN+C(;;^jre`h?Ll@hNGywyp7s_9W4w(+g%Jb zVhM^n%!<>ndgtwqVcB|NnXcW9eRE3>P=&J|eHF#)nFKJ$Lz_tWqYUnjsh4`!m`pzz zy?(5s0KZ@0xJq|+g-Zo~tz7u;`dhZ-SyA!+SyrzudOKbA1KeuVg}7!KnCe)YmSWG? zP!_#FA!;yGK4|{TtVVj)AfCE5aaBN~)Keefe*$~G{Ua9h7C}__aHnRtTVMyg(@sI= zgESnrPN&bx^{>M{SvYzVd0=JiAj^{ubn36N=nv=Z@8vCD$W0y{+9?nDaMHLIXncJnH=Q@CMx+CmY5?yXuNl+`r<76kT;Mp_p=e$lTZ^{ z?6{e*S(c1BVs%+d#D*bYFvk_Xt8>=z$@+jWW%2p z_#2{hpbvuEl6s)wQ5Nr^`L^OBvValE&wb+FWj7a%D(u*P=q|d%(uxZ$!(ZR-GfK#7 zVC9TmDCfxW6CD)Qa1z=ZtdO#Q_v?-9q5#W>H$K1=y3yb{-*$X{>!8>fPN1SY=2$Am zT88@E(EkiKd-FImF)c%_q5RE<`sDtI>$s)8@<_4xQXtM3^yX{2ZzY8q4Co!}l@pOA zb+tBKge=Zg0nusqljBZT2l1nF>h?y9S5}{bgM5=w`ei#uD~8pxEM(+#Cvo){#*&83 z8!#er_(B}JIIDUk%TqO{$STbhI=?yhv|E&SdZgnoL1)j`Yl^G&W+y`uG$bk=0V*&X z`9G=>W4F(6JKrJ(5hL4u(+)<3J$;EaS{o|+x`xJ* zfWaRZX-(onPPfb=t9gJ1Kw)15lk=+%`8m^VZ#TW0c309Wo0L!r!8REC-`~$?bBEF- zStr5J{rVZ|*EAmYS+c$~;5J=)!mPP?(h(yoT>0Lp?*0_e-P?}wim$j6LaT=;U)7Eu z$g6xLW!IqaN)o}ZT(M*`{JIhD+{V7s!<`;e51H~rxJxDR?U=J~uS^|v8zcfP{JT&0 zv4VjCoB2t`kHRx1O6qSq3s$^wAO7TWfCBF+#Nc(?CAqIl=nb{kVBf%>3Uu<*ZpxD9 zMt*5>+lFKAihpUHvqGPf{eIwVZ1wbhl9RycS_QF)MJX%T#CNw2^uFHxjoT??^Vn_!X)! zWEQ#S2U1-J63w-q4jw+=xHtC{vC&tNd!que7NM(2lzGN73rxbkokvd4WJ(i>-a3hO z3Jp~e-g2<-*YeJdjIBG#04F(+xfS`{{hMHbrvOiS_vbGH4mk+3P;A=;@04d$v%EcG zHu}t-qXO7m&)Sa@Iy~X@uo=PfZMT+tcwf9;<5o zDB5Y=ogk1U4k?ZBsEf23wOXBnC1o{9?H3B?>h znYk%tlRahLT^@Wl;N)RnS

@#i{S^wD)>yG?UsJ1wBf`ZR=*IdiI{^NwiaC8kv%7 z8cqHW#!jT&I`&c^XF8m6aZbUBW~au}9Dxvri1DsLvd=CcL>_pC0y) zbe{1H9~wKuhoGsnu)N${qg1IPixL5#@pTUh(yuE>~}c(eoZAuRd18P&2!>FT&Tq zT}EmK%o}ch_<;vtX%}NNpxTtm#ZXCeUG`}Gbq&^7@so4hgiV~ zXK$L=TFH~E#1ZrW^!Lkf*Sqk`2iCe*gWT!1RWGhM3aq5kFTlEq9#VuGqs1Lni^U+E z^mL0XcG*ex_lt*1#zy{t4;cyf_D)o7sQ8@T9K|WjH%o-2AHKPt`-1Vx0g=R*`gj2s z3j`|~k2kDF^HA1p_R1^*wN4ew*NF$1Qlk)T*p{#x@qmY+Fw>kd-@%lzP@U=6cy^p+ zZY`Um=~4~Vlk0k~tJnz9FrM)_W5z2#?RxKCtp z;IZ8@if5h-i=TU5DDqzRK#jCrMV_WgQ%k`5Pj=#jUbtTth0td`n#X=z9*`2Y_ym7h zy*oUkk?e)c5Dzyf>v9xWG)k!his84M^l?8fXZewI5;Ycgo@YjJw&NMn9IWomd#fe) z#Vck$LaUe8W>RTPMqMa3-xRXV*=(2Ky_e;vV9xk$;&Wscra=Ix{fH*M+GHTc>FnK` z=@e{g{JSJIQ7xKuGYu%X5TV%3__v9H^<3NK4I69|$U_$Ny{nqy4=Y>5fXF*;wpy2Q z7jQ=ZlXa-N*)tXkOuu3RGAVYQL#$v+`4%w`O_!+{I5>NFGB=>k!cFBBR4GvpST#lDS*#^m|^)a`FpXIbMECdj$+(! zOsCJInbksuxYd{5wL>_=?;f53b-!A7PGD=_>+Rc#kPDTf9TlZ+gY=o%&e@;4NIddS-!^#uFarV=qm0k3jQHcQRDMy{lW%{P>QZ3`h5neS{J3# zF43POD*UF#b&<=D`y8$$aeJ)Wy!YD)hQ{AO}Nkv@-*$U zJnf>}B4HAL(8T6{vwnDuS!IDSieSZ|A)UtxnLdPHgnFT(rHS z8e^LQ%am~8z1DfR1D$>@zp&KUJnj&9vRn7dJgmU13xRXAPSK=+`NYPfQ_W*|bTtvw zTl*M*E&#GbJC=d)0ba+?sOl|`3w+8qxzkOC%HuYMDqI-4 z=gI3tM-g7K>z)7tlH+xV*8^Qwb-#@4<&(Uh-Bxp$0jq3(P4WJ4%fCs#NQusMXc1jA z?KJ{1DViT!D^O*ko6kGtoAyAd**E(Mqf+7k6>$p;u8M1pciBFhe8IA!{A<# zAC56UEgM6;2kw73f>c>#tl5$cFIn8{$)8{qr=+kTZ){oeO=tW&*=%~@xBf1g?4RY= z-l(%k;>B4cF_3x#rjy(1ff&BVJjU(jNyDEX!bfJgipomTfKMoTiU`jWgq|3IiUJ0dLn!z1z_f}pNAS-a05cE7z8eZTR9^O`lz%fI9pXdQJO ztNea|0Oag@F)l`!f5KQeyfQ!lysj*4agwp<4~YU`giDv+$8KJtiAMs1$!z8`b_30K zD)sHh6KXC!bR!w@_Rrgp8OU;$)5)q;pqm(AIsyn~Jwo+g{*G&k6M}~t9&+QWUplSy zDkV<=27m=+$@&`rcy#uSRVTRM_JCRkxiB9C$j#L7)b{;n>?W^m1vD=Al3e6X8Qf%s zes0;O#(AJo#HpEx)9Ul5Ttj!j2G`)BdRZCo6YK1Mh5{9_=@SPcyman&3=w!lbQGSu zJI^`VH{{5lFXK%jB=E8}jyaod<_#v(sc&q0&=F-$k1{M)6Lc>x^_G8zzAOq*zwS>& zdtx_HqrstE@Ty2ho9n-=!s%dnHImx!(SkuCRK+M?&oWTO>34@CSl;V^fIZD;T|NFUq^bPT}^`1`}mvP zgckAwxflVUA%f2D*Jg4i##_dg=3Pf3>OKE2U!ZDs#*!hg@e@IHhZbh}0h6Y?{p1j{ z?;6kp=LlQ6tByM9PR6PT9?tY6A9mNWqkk56qR$@hx*!0A^_l=)EUn+&J8I(B9;Ml7 zTeBJ#)eb(vRX;A6o6^_)r2HBrAGNpX+Lp2w^3KrmN{l2M+r%?M3{fy@e1LnZVoH`|-Q#_s)9Sj$m^P4fd zi_)rT_jET$nxxqmef!G- zC`$jj!EJSkB&U=5x^Est={S)SU_=o@wQm4Nby%t*p=ZHllp^F|HSw47hvA-24A&F5 zS}@V~tPvVoj^2q5jrbR_rMlb4kr)PASV9TmabfCf4Vk6|tl!9mm}GU3H~EDE6@eFn*#C1;PG45eP%>N;@|IJa!Haf!@Z2-|Ah_0M+ zaW+9UvZAhG`||tW=w<;YBo_?g$nT?Q6cE{HZ??MhdAr5mFXvtsX0qs2&9S7^`^HyJ zUN&;D#b80P(ux){dKG#7uTVQbMNiB)!uM#{Qa~vI!Hae~B_7Um898ZR=Zsep3RujC ztC6ED6mFV&3NTDU>Ip;8DH&6k~BxnT|3w3~A1{tus3FquAoyS8XR~r$Z%ThckHh zQ50W7PhNDd=9Asfs6x9d@qQq{?hAi=@rm@SQ|FFhCbGbd+ zpE3EFAv!|`X@k2E_T|x8olu3=r>uVZ)rdd(>IT7C1J!LFMaueB56i8hK#Sj8JPeEO*Ew4n zr1A|XPa~}1q^&EBJ(9>Saz*`^6+adhPzn2oLR`U z|ErU!hTc#1E!7wp()hi$KcnyBtbSy(E2%Qs=B<*j!ioN!%?=lZG3=6@Memrhif4WT zMW!uKkpI4m9r^OLJIlK*-QR;9C24K$dYSi3H8$z7eGU%5~phkpblNa^> z00rWi&uA@C{rv5C^m3T_XOp_A1UKE1I?J!O;y$wcy zQ~@nBYczaq9^R*y_^bLueAXWIR?v47U%Z-8ZF=|0@!RHdap7vJgqKNUF)pxcsD%(d z6H(!}yIl;F&jS#aKCCwi?F1g+_y)>R@ku5bi9NUm2JW#tp;z&j6m!MFXJx!Zl$f}Q zK7v$Do79(J7Bs!1YiMUTXXN|U^GEk!&F2HM*2V*O#QPkwfwa;2@+Hf;n6&rJIrX!{0SNWDQF_H@W%y(`MSv0i< z6Wwv?#GM9aJI%>1^<&;|wEso;`L_arLm8-3JbUkO9q3?8J2+E2FPmJkP2h{x^>^P= z5egs46_kqrN)_>9Y`LzLLBYv^EPo}+V6?h1pp56HW2!H-grh8}i ztMzWWct6!o(6j$89sK)nfPW3A1$L1}P9jE(@{erdbC*R_&uR_ciV}{=tRI{IQEUH? zXM=_d*w))YPC#P4_2=)jNQdcHLr(Ytw!In|M@#R&g##_o*~#zQ|G|8e?EN8vnzM!GIF?}P3F(v`TwTDi9A_6 zfm#{<|6A<2^Vk~_vYzQLfDK%p?q4__o?LR+0eN3B7=yU_DR4R=Sf(?Je zHWlSjdA=}j7?@f7hdp5WD=_HWhlI;IRAVa2f78uY@H>5=|4O*_HRerV>!JHIm z=KVUTj@7dI({^W{T<%Z7R;FMFP1Z0_%e2R*Bzx6Fi_E&${-J`e?f~bS-3aJZ!`D)> z@Qg+PgE=7XyxPanKVLTGyE{0OngNpll0mFeQA!wdzPT}Z@pYK+5DMy)$)73+9{N^L zRriSd&nmMb%JZiND6Fg{QQNurl`yrqP2S~k4@%(w(tFIGt{1U3ZSa>0)&|A#{*MBA zPHv7q?5Tbq_Nf51DAwt&*9&(RI>14l1LTYi5slBWza|R3;zE;3mMywg|Kwi+KPkUG zWS311C?CWs@3JhmOx6nxYVnE;CaW-})p{?(O%|WzB^$Dw|6J#FP*36V?=BAiafSZp zW5nIB=I#~W6RvtY-x@cyH`eo?@Os+1}l)qP(>8m8+_XDpX+iN2!!<7&pAX9Fn8CrC4cQ5~lo zKowNn=ud(fbZR0g*DAG!raL`Tbe3#iTH)(Cmpr^cnMGW#<>1DnRc%O z&+9J#drShHN(At>81q$a9lc4n#J&V0LtH|zAwR<}{nruv#|0a^19<3>xBn0Ii!cdw zBAL99^`tWVBK>nIyZGa`KR0@KaUS5A>xbEfR!dmPja){OUGGwODiM>{{_u~+@C3kO zlXR*7zy2BjxB6#P`i5ffQ3$-PpR;*ZGrq<@P~FF8ZR~5_P{fG{LU zxAN@B60)3R2A^P)HJ=Fz@xx1L2B4%hax5@CJas_KxO+Xl6Z;yrXN4-bbLtzGPD`pwmd+F)DI4Ca9Z$%!zqPE7Ar;o6DuJ~KvR}&uz z&!2YA5`M<5YS%VG_{t9vQ|Z3Gyx18`O}`f-Rrv%oPLvE2uhB(2TrlV&JJ0W%35n#liMj*#_{pHxht&9$%Oku?tlP;cMUbNP1 zIlt=O6BOYJc`6NrZpH@rKzE)cJz#=lmExAIWS#76eF?+(P4n{#9pKl} zh+7CQc{ELO0K{t`jymd^McPILpaZLWyamSEAeSCjXccK}S1+#>IAYXIH(l7*vzRmE zl;-0cASBwF38vDE2P1WG8f@oiW9ZP2y+_Okl`x{q``GWKNb?~~?&tt)#*u+@ z^ctK_%GijDf~a;=cJEH!IXhY3lix`N16PoQuwEOf--Oqx=edi+w2n3e?Nn)|L*luTJRj81!^zHY^#ej*f&;>&xbfU^fy|v*c!0crk&|CTnyZR&0&DEj|Ys$(AQ59p9@>U z(s_nyV5JCokFA}~yjl~H71y4;B3*o=tpYM`ttM9fuWk7DT!P)ZtI?uwV1l|P(N>AFLcIzz zO}Cv+%O=to_kp6{40eGh#4m=0({Qtkn?INIfv zU2M7CXamr(Wc^R;0=tSCp7XzuK-gdt*Gl{?nWIuqPx>6!K8k_PpY9Z=Dv91J`9Et9 zZ$qXf_EWEc%?q zr$8S&fHb{u9-GF^#&X@l=eSWX`K)Evb`ti*H(a5}s)iPI{(gsu`5nrOBQ!5(6nhVv zA5vZzd77R6w~Lkfw?q8OK&}R?__SyJZK`)wdt+(`pa)?DaLyieX8xpCSu!Ji2V}rx zie#TMp}O`xmBI0LuSy5*6yo8u%zD#hq&wjvuu?;kOUz#B0poF6hN)K4ZKW)2yvWShAoydKtOeQzYZ}JqblY)86tS8rxcou?I*uxVn=WdX$2iX-taX6IwxpNY-sqr^o2*-x)E8@>r2i=SJboWr`RqO~ z{NKF*(gtN>OfCzJqv6iE00$bF#}t4;59}(U+2gfzm)|)+1o?KE9a|kCwUOV~uBN`2 zZW!E{FqoUPUSdzPbp+QaYvXOl`=ETw4E3YEb?cj7a57@z)2JIYG{ukREo1D1n+zJf zMt|0Q;X@awWm!Aa`LnI^{0arfxaH58&ZzYtqsnga9Ujg1YxEzy!#u?Etsc0H*qmkz z6foBb0b1>Gg9G9_3;+Gbl;g=rGQY;k>b<>*Z=TIR$y+2&k5TMvt<*B2G13Pv6G5g* z#vdoDa6Ti>AJ08%}hco+ND{%Apg6C+|#KUGxwYihy@sFd;G)(aE5D{-wO*@s* z#c0@B>riL7Ap6?V|zHQrQI zcCKwu@eE8D+P&@6B9h_;A&)9sNENqiXBJuL=PAsJ#5}sVEH4T_v^d4fjyet{3;jZx z_}UhanZB`xwc}()H zH%mGro@wZ9R4#c=E0qWPmv_aj!jD^1vQnGxNm~>^38o1rvw>zanh)JYO!v(THa2-v z#L>d&l~FHlWPQZ@UI`s9ZSd6s}BlZkTl> z9TT!%{^s#Kz`emWFCGu{GnH1_0f{QN)iNO0dY{mwU2+zdu=ww62Ob=JyuBR!#bd^r zqNa~3joe-V-izx8nhkDS5bVXSj(|)@o4Y>3>$VNmAMbu$_>dEj>E~=fpVgrZg*zCg z9c&?3rDrUr>W@j}St*)_Q!Jn_4aqXa)#;uSyB1yai4Oxvn96k0!04EJG2gO;4&1f+ zW@ZJ;F=3Px@+{~O@@@4X3=~4RK=3?qCWyZSLb_(F#{rRE8WC4L1fj>#epKiiCusdq z0g-a#mrTKD6&W;q>g=snIg)CGSr_={(iztJsmZM>)Wih;)btwk@hO==z&%x836C6{ z4AN6bg%1+m7_cXNPO25@CWV#A^Ld@TOX6N@nb*^Eo@yu@Fu_a@zzqiY>L=UVqz(-| z9`-9y%h2=vLiHj~8O(CP`7L=Ht=8}iG3w*By*>eR2>y^98|^3F+6~cIcMPQH8iT6D z?TaTlRX_)9C!u6gQ^)9OmA2h#+n{+SznUFB@8`?B0zd>ADh0hm%)3z7cAiPfK^rE`) z8gRzX^sd!8KBNmC-;#Xgou$isHmv3RB>`iIiL;)e?gtntYRJdMXh^F1T9d^f$z&)j z*LmcGC|G~O&EooWCq3PC=5!~vf!S~QH#N?JxzZNVfD||JC$={3M=%E=Eo*&;i4l;- zxT*IY3~fQbz57T%1)f&owr94mT*vd}`&}{!kXM%PTHQjkJbcztgHUdnWo__XACcUc z*|44QB0yG*|%RfAL(wxyPQ zbt`6VQgd-0KhRV&y4cWKL=1j!h2`QJ!`}%F=UGBSpR1iOmT|$2EcwYu%bvZT&nsL5B-r(J_8i zUwcYom|7)%k+nYQWA{xL@>_aHK23cPe8HJl06lY;|bkI~3QO~PvF z4!w^cuYLo!xB+z0#buw~j3LkYWdZF9e;K>bF?eZ8rEJ|9_L-FL;-Eq!yS9?q<|Qp9*tjs3KFB*qs1sKe5uX=%5N=|Z zrJY;I#&ip;JbI|xTtBX%`_d4eU)|61+@gC>cmT0t=GFA*<#VWJ$gXqXc;??tQ}P`Wz)495`(?f6VN%sqa@B?ug&4eFY8oaZsp4!8;yHS3ClA z!4h`3VK98^@Rpyfk3}yaBMtoEju!3wQbl|rpmXwLR0bW~#=<(*s-pnES|aegs`uoAnzo8_ln8Ll#( z2Xk&~UV5Jfp|yja&=v#aJ21M^dGHvFEI(DxYl@c}sPOn9EwFM7DJqqrB|41xbQVw0 za8{JtIH`TQNg|YOR}HxeA#m?l?ncYKb@)4_E&n@UGj7Kx+xn0vCgL3w0<5BC^F1N= z57vwTo}L-fe`4CGa+t(!h`742s)rCW_;Ozlpyr}Nv7Ce~it6at%iOj~pLwpTbGSP% z`I~!vzztlGzpm%JKXTAP4NV59Vs~y>Xm7I~N7I{sk z5B-U0=__2GB3APS@3$BEFKmLYK>)9h4Xd*-nVQhp?-I^(nlIA4i~Zua=!wg3i(6L> z+Py0%W?NZT!mGa1hAvB)lhdEsf?yNdw$Yv%qSZ_XCU&4F#7(jta^CNP_Fb>_7$yDZ zL!Z^~4UT(kH<~|{SkHFD%%ohZIX&UMA47Mbt?0ut_Tb1VKEJy6QB13#r$+A#h7me_ z_ygWnh;o|XP;=tIYN$1cW<_;ojhBnYmul!yQ4Xa$*|0Y-lS)`+A8n*&YpgaYD##28 z95U0xI#jn_s7(Y1WUPBUXm2!f1v$=*O6YoD`aNwRpfzFOnvf0CffFXrafshfEMP6} zx88ht;XF?q`$;ITPQ7PYS4r9T_nLK`OOcl}+5MKdMq3AkWc3s5^-E1guwy&my<-?` zd#L6ad^{|kg0`BNa=U9bGV@BwU47{X7YFb49R=vdI}Y2`{g7?4EoR}KiZP}%rrez2 zvgLTi-m;0>EG3bjWSfdx>Tj?5t(^i>Ec;di){wL?E~bNR|7u>F1QWlXO&nEf!sa(b zgjO>Jr{M@1yt)aDYNgRK5nL;?-SV@kbyBAI3HC~4{#o#&Q*YIiW*G$i-L*G%(51b> z8GgTrztx_gzT??4wYiube93^#e=C$jeLu?#5tsgzZa^`BP6v@Xw-l6F&Xm6YtU^)^ zJG+N`fSx=OF%=#_@_>vn)M<@KMi0{W9-^(M`;EA9kKY;_p?^dP6-ShoruFYct4V-sV&3 zaV2THNpkqwN8U>@uES;lD)g|?6QYKp|0zBn+O4Tq(A4%UT1GtiF^f;l;oDlrl7S6J zuti_*@%(|$L+Q!2{I6^H3O9eR17GKZZ!2CX=;(bx#(iT#tDl(QJ1F}s!X_iq-(>v_ z=-@iQ{(fxqb+ViU4oA;K*T7o$j-}RW1?XQm2slh*6q7PdE{k0JJLY)w@4m-6uXBys z8~aE{cX7gn8NP3R7?#Cg0Ub-QC)JfuI(;$egIUz}x4@6H6HQ^aYzxDIsWbj?&(EasyPi0yT&Xqd> z=|oCo`D%F3bM`A!+wTk$CG{wwToQ!=?~5P%KhGLU9d3YF*~|^k+RX8TlNuDulHDQP z78~BxJoF8(VJ%ObDi@%CH9qiciVi80DiekBc1KpK4l;Fq6;<-FMKk$yXU__STa5!OOC-n(hR()no}sz|OgHtatt9EmY9O zy9dQn6FOfr%j4L+<})76HP=$kVb)U`2!Vy6NC4aFo|hrH0jZlsjk3G*ji1J#!V_WeLOx~88C1S7<3cT z%P8=22xZ(A%RS(LKYTJ!auO0eUQ$F#L-Xy$7F$>j8qx%DSIhm{XVH!F-z>@8J5b`5IT&}%FRoS^oy*t3c_25Soj}8%M;$6M=$Rjx*pQ60-_IzR z#7#GC-6eVENfgB#HmyvToqKyqc!A#k_~rJOrKCM#(f-BDA~*i_3nKq2uBd3cj8@Ir zD?N{UIYYw-o0I1DyY9YAL;!$Lnn#iB`V89NY=XQrOA%t&sIK1b)!#V@e`besRapRiFLf(Jp42!Xn!yNc(l} zbavm`-mq+=`{3*(e-jbUZti(E1*l3u`+SbPcS`cKu`sBvJ^b0Mbo`xkLgf4l zg;KC>!br5QL$8z?tI~&Wf92EAR|N%MIo(y@!KHsz*);V3jcBHe0j?y{MQ1+ zV)qHMYgq%T)g{}&S7)BWMCtAEJ>NdxjYxsEB~9T)b(Z;TSt+NV7QBaxFVO&q^WAWmQ(eW(uDyN_xb|a-?Ihj}kH2r%Z}TWgd_~Tp z>UDh5;oPIul2NlP!^Z8tO7c2sqFNLQv>z(Ix(vO0WL8$jys9-Y(7#(*9B4RTRfrMBzL)f~b0mJu4B-|Hx#%Ikmyl`5+OW8T%FZ%*($$sAT ztYg=R&CP!ew8(&pS;~EK$}G#pUO__0s-CY@9FlF>HXms{l%s&AyDa6%eUoUo-?+CY zOSN2(&6824Zr+u@K(NFWDjY)`g2U%!w?m(gVmxJw-Z}kZe6r&Glb>UQtF(Fp`j^ts zG~7SD*RdsSgq8cGZ+rlxJE^-9x@NToIo;*`^o7E=(bgkSM&`!JxnL`U(`NwiF3^|8 zjV3R?L>mc&^Hrd$9FtFM&+=FYi6@fuM+ZUx$0(?!?9XM8~?pW`j zAZjz)Id4G9vSW3T*I`Mu+kD$~0{7E@+;F#zTQLUu>iptJm7PS6aBajzq^Z2&tE*5k z-`MUrE&u$4c(VNAIJlnn@DgP+S6|blhKZF_)LTY(*QJWZX4km;T+Y3rM(ze%u6?VF zL^UmHpeX_4YP?r4W~;liHZ=$bDw6aw~U8fVo1l4eVt1 zXEHFA2*Co!D{SJyBQb{mcGo)Wc^3bU}W>i@hX3PCebq$ zFi%ud%yTGnorIOq4zzqW)tn^UFp22~+d5r4?<)MaaWejGoPfI5(Af-Vx@g!Ilq8ep zI|B%H5Yv6DIU8T7)%*nBTx5zr%M@xZ?3-nAa^fZW56mzZcRAsjELXZx zmw0S@kZiYDIs0%qv;**fb1wJ?f@Jz3za!qTd-?B>IiG%j*jw5!7)}%JxQp07Y;PP< z{~>%Kb^6x%tJ3KBwg)jX?BG^s(jcOi!pC^lQG4NzqO-5cYs&M$EL+IU#FZ|w9#kMo zK1odd8e=9MSy#CRb zq*o!yf7qCSf_xhb9ywmBWw<;ppL11RLU2F&uAZ6;GRG+fR7-r+JY-R^us4PW0ZF##O>x=JdWTyYr{Vuz1U@XR1WPYlD{BncYX z+&;cS$4;fr?H90zRj$scWjk?MrPW^E+aufMT-8jM7j9F&4oe7l7;vh>A3*EC^SWXI znwBw6?EN{QQUMN)X7q;e`hDB`HC{7v&#&+^!N&;h*LmvDFCILE6Bq2L3yzkMa*E3n zy%2F_n^2W3(i!@5_UYMR41?$Y8jMkIqpYnb1Jm(E2o?rN9j|}GOv~tA9^^hgg48I! z(L^0r;f|(OX4UtclTlssS?!6HcH2wU1_{(Ai1IIGa^}U~IFAIM4j<9NsDDI;DfBse zhl#Bvi_vetkH6teT&@CIUVnZ~IqB zhkHRjQ=@%V#;RO*@DqgIns`P-L`?G@Pv<)Hv1`93THrGO8`nDO)zz>jE88Z2Eir%n z<@#lrtGT=1jClnrwNSSW7|;ZEOQ3y|vY?(StA+!}JXSelbn7O+Rw1PDl`=lgg+d@6 z$EL|P!g0~M}A?v-cbYW8W8~Qtrt{B`}J&i6!bdkEUkY$T`ZSbKAC6m5hNj4 z@tT=7j3#4C*?Zlj)5GlxKR-VrE+0L^w3ZaTm3w}DP|8P|;?RX*YxRb-kKPSU4F!e# z`;%=h9#1IhUu$lGP7i~^KL$nY&+C&g*n5tLb6f8{AJsHJ@I|UWO6iwc6^^sp>r1oT zH=vQU5`tzmCjVdTy?0cT+tw~_LC}qeZdA4+1Ph=dMCsBI5tXKZB7Gyhmrx7=BBCH5 zpdz97DqULW5m7p!6IuvL2_b|CX|#~v%RcAabI#VYf8QPB8{^(F?)Q%)43qa=Ypyw; zIp;IyTC02|L}igTY{HuPG0+!5>uoHJE>($%#-Ed8bob|Ar1fykc{-;%9a)NozLSJ9 z`s0@?ocH0)O9#=j0z|no$DQVl*#{qbE~c6IyDvuZ<~glwPUfJbhBhfDnBPA4re6y1 zjA@vZQ;QZqZ}6x%#+Fi_OX1+MiVmrv0E9ooX_H~f;pg_?WXcvWn`-w*`ez^bS0hstqC&~&F#LZl}f|@)$dx&+Kq1kL5EE^hK^pe+xv|2L^FOy z@vUXh0lXXUcKT}lvR`yra zPA{&(_Idyqm0Cy{98W zGq?{BC}HMC{T1H?!l~h^H60=mSfl3#RF`#8;v_N99f<u(A>laG|+-2LOW=B!J^yE@eh$Ta1GH?JREw+C~{8u~PMP_zp9m07N zI%w$b^i5>P-<-3=Z;73YAmxqW4l)t`5@%aTm{HU=hah|X2bMBrE$%IJpW?DYh#T9% zhMmR_@;}>oxZZwws7$rWHShNUchLN!cBsw}>6g>r#3iHicOHK&5Als}<(|d{9h*gi zu)0dZ?Fr5q>%1k0Cw8>IYHc=pK09OnFwc5vl!p$Sc9b5(w;<=hhZ0%K)@#1fx*}l9 zdqPIe5584>Un4h@x~h$L7LF_+4^SVM^Cn2>%jZ}|e$g$SNiT0lJ=0)Qeh28EWoc2C zghIXgjwjtYFf5RD{H1=&=koTmOO$)4oDql_N0Vwbf*h&vnG22GY~5zYG%&5Ers!iEh7ya4ui30Lg?CW%KBbEcJ<-TD zc{Z_##BK8U7!9iLf5U&d)}w$`5+p;Te+rQ6p|(wr%#D)Be!ct>bdp|(uaHO5*1h-K zD~)~c=f&$!3}0O2>OV5+W(Di4+n}iz1wHX;mCtLdvuHHnVAypiJsc-}gr|#b-m(ab z=)&3Xr4ZMuWE#?V;R@NKyIUpvw;&~f)vWd)r&rkeLD|#Av zZX-AAnfN^d7#Y|w>he&eV}5S-$Xj@7K!N#-Q1C>u_WmB+PJ$b}xrEnT3sj|96ZCx) z{MEbP>lcR7c1q*pF7G-E!cNezjYpaVW-3nlHjbmIjQ7?s=hcR%54*o9ZhvOoL|ya^ zLC^-7CDAievJqazvyFLmOf6JYWlwF|PR$}sjyk2dI<~(UrM$o>pyO%nt<8E7Jadbi z5-+~&)YPs zZ3g(GvQR9*>|dGo(`y}fz&e*V`3MV`Nx2^Y^O>^jW}e#=>Q>CwnUi=*wy8MsVDY7W zZ1~k^sTSx3m1mUOE)Rw;rayxqwSr~M+`*^+c8-4nSrgm(&S~HjQ`XTb-(B0sgEzIpuSccSD1yG= zK3e>Nq{KJud=AZLiV|M-8ZvzVPUBga@|;W{YE`<1j-<_Qy34U(k;7hx^oUW>tSWUMGB+T0YS7)(^+wcl#3ucO(+ima$(yek#G z+tXjwx>p%GV3T5A@R8GQAn*4sO%MFG`tg=gv6AAyIkZ<|d3g`pL+;y)&3bkF;3sId zg(bwWPdh}56ntiBKTAk=lNhI*rejWVE|bIVyj7dDEhH}o6rPuhffdi1ECuCrdm>I@ zBm+&U&G6LMUH&6W+lile1A9Q<*sj-~QK5RFo>SvNQY#>LM*Rt?bX`u!8TlIb^Lopk7>M@*O%Z*Zb0vidjV@M+d5$46g=H=ENuA_Z#pa`_3e&;V64V4!(sCaHmnSn zZqk5o8h9&a6+%5vuE+L>43HcvRCrm z36#aaKuA{0Yx=K4XA8or=o1R9&s!{$?`>eYQB{-|f78++hcnFe3`6%8=iUQimi{!Q z1~V3jq=d38gQ>IM5Mh)P9>k0UWhyomfU;}-8u zH0@o}8gal4#mqOVQ`HptxJ3Q9aF>-jOx{-s4hH|Uvv?iZ+aQTM2;rx0j5 zhzuDJxK9W{XfHxOwX9Xu^=GkPbo%jby1IoA`3S{Sh>?g_rP;TX0Uu#c_7LE$>-|#64KHqQtS;dX0l`4U%`^n zRk#3rrNq`(mZAJbIc`eI@BzDDPW~+-we=lt;IU8iRHQYIa1!zur)l8h8=)Hy#^>X< z&s|#H%8^sk)o7RMA|l$;Pt#&Tf;_QXIo+gc4SmUkH(R~nFH>b72XQ>1TxeRg5w3wC zt?k|KeD&}(GIU#w)uy~PCidR4SCKaLsBXR{%%IXn9yQyj3A6d^v#_s;IN`HM>^ml6 z@o7bg@|21Y5O z+iw+vxUE>%^Y<=#=9>!%Ey!Qp@nG>AB~@7QP23?^z77p!<%~A5KEyY@q>d~kX-;%z zW}W>5&lPQ0=JQ^Z?nnGo+D5HG+2JemIg_Ql)qcF_-YP7?a%^3RQXQpi1b7DM(zz0X zDrU@x3s~PF4PV#IcG@AzUi+PGjfHP6u;=Vz8s(``=B^-o<%1r_RWfn2ya8n)&^gg?!B> z-+$obkbVTB^*FD+AP7y2tvf4CH)+QNDzVG2)7Y8ZF-v*XU!=z-GP>m}D0I_^B9t>< z68sZ8ULHj^9OABVJGgl@VQ0Yv&-IJzZ1V?NpR0LTPT&8?y<19~!%WRW?U%w3l} zF`Ee*qdqGQ`uL5}wZ1syezTP^+q02pIp|Pk$iFT4O&StS1-)L}>jKi=G2q9WJmP~Z zJZsZbk-0s(G&ZW7lY33zYHkW$+VUWQ*K%S=JFJ}8McsRT#j~96D|42y57U{R7zt6@ zSazm24ta^fv0eu5Za{~PAE>ITs&i%%^u|wRU4Loxy;yrvBxxz^dZ63V^>Tc${e;PI zeMZ3~XtboKEf4Fda%j}mzw@0;{nk!CDGU_I97xOCY%veh#;2j=SgmiB$-u1@JkTv> zpQ~WjR)N}6&fcN2)YqhDMd!PB>Rc(v77%7l)LKV%B~5F`a|A3Xsyzsk~`kZ=5x-eBzXx zF{9W6(VqmMf%ZYJU4F$msZ=HKWo1u6np^57((`#;?YcpBdqk555_pD1wD>UBFV2HV z9Gt6Q5!iwjI6x=tO*FO36VwFtd23_6Dxp{}BE>y5JUNeiU4TgzUE7GCy$QbZRm>bCmy2)PWDcQi_C*KPbT2$vy7EPVUAfxp}Ih;lmiT0d!QHbb)ed1c}i@8J8m zYy#vvLDNmQ2~D8rcV7&o_3GvA1ZqMCXiR-FX`6WK`SqPmP7!8qEZ;WR5?e)`4B$c! zp;G$aiutEzf`U?Px=231GIO88a2b7J+51a*?u~DWup%ve1|aGRg@#hkuOFG1Re@j!IzGL{!8buF5E402C?r=+7i)~weBuH=0Ftkj%&Y};Noa{P*Kwj zY1LH@)PKgBN*Wq%t{&NOHRiEFo=j-qW7$zobQ=Hae5r;~o4ZYN5wz5!1>!-I1KQk|@!arnUFSnMEnxgb%XO`#46o`ogo zZ^u)@pt?))qJU#QL#!=xM7QqdLcHL#`n{}R^(3o z7r41+w+|9HL+34Utl%}8ka5B+*)mo$-e6qgzE2upG(MA6_zDZroGgr6pjZQTdr5*x z&aEk1bgycLCtZV-icRKImX7Ax=qJo(bVqC{4)@7yBK1{745b!f8wPz%(?Kn!0|DZD z`lR}@zG&qjHqtG$O<50XXB3xkEC2!9#D2@`5dUtwiBasJ*zOjB(8mVm!5)!l9w z<7aM>Kq@H(+>s@=?@cFaC$s31?0Pz%C=8yP{-JvIT@ZRL3~-rD+pE#4A2^Suv@3i9 zg4XRdQ8@337A6{=HAIV= zq9La&zkazUc>q6Qu^$(VNfAqSsR}AIs?x=%!Rz65hc3}%mNS~p_DP<#lpJc9;@Zp$ zOgrS~hGKL|d@W{uy=vgG!g@DM3-AEsIbprDJmfzqK-vkr!lh6pLW@!tI6TEsqV%MQ z@CD_F5x1qA<7}K`_?gD3qU0>4(&?b<1F-8CJ@PR|@rzbAS2^&|S4 zeuoLpFqt~&umlG4&Q zUqmAgTz9~k&!xBXJ81cnhKpj9Q%-+U6!BSzc&33Yzt}Ecnrn)L^Z6y-SBe1|Y6mPu zYb}H8TnE8ppi?x|U06i@22G}CE9a^j`hFPgEhk#OO?-m6)>9%fbS;WfKaVIu?s)dE zIc#=kiD{2tSHgRHlx4~u2?CBMk_}V@2+qmHvii~`(;b>t$eE_-8@QrS4#Z-IO=h>Z z`fm0)0ijgm@4_f)AAw_w3~-Dk*sVbKcx6N1?P~Q;@RhnYalO_nO$T#!?v`g+slp9v z+!!~<>&sBdg86BZ89mE8b#DoQ`ibn*saM;(0P;BA$un|*5x%*j506^(tLkT{ZB7%; zQ7mMJltZB`@3KSr)ytVj{8#5FV9}Bia?M$0O~q^mr9Duf-iIzJWa^(dJ1hQ(lt+Y3 zB749`Ik;eQG>d5^1mPhon#e1(Kk6K9_GIJUR+2}@-iGir1%LvSgR?5JJ6GKj z>EOWnQW#kY38u*2>q~ZA;1$`F?FUEwM%&GGH~Y@A0K0`HbxmU)G9VYQ56D~Td^97V zH$2&<55$@p?2DZkQdz9-5tCfPYhuz&*&@t$DZ*pI|DXs%`3PZQQQD;T(GrufX`UeW z*4pH(7He)Eg!YDOy|xrCC_k)r{EDTD>QY~TeP1H)ZzlGY#V!jY{o1X*%<|V3$;hnv z#lDSEQ)IRn`OQugT!WOQm^X=OGUYYZd<46kgHhx*$rE4*B)i<JrMrIX+x^FQ3rY zuTX|WEb2|uB9k-jsRZJv`!i|=C`Pc^hm4~%v&c$w{}l%kMo25Zn|oHc%|AMHJo;k$ z*&(lyq6gh}!pki2;Z^VM0q)k#18;&pu1_ylhB{h>6;ADx6NfjdZbqg>nz-GDX|$c9 z)}g{b6gm5c5P5?=E#k4hi=O=!v+@CJaNM}b(#i+wq@N|fBWihw=9a$gSf8xPvU8~+ zuvZMH#LV`_Lm>+YdXOLG_@@$qGA{YD^O8F6LqIy?i7q?rhVzgL`Dh>U?QH)gUe`^( z(s07SMlVApzKF>0YgtGM)E{$w3FtHjJ}7@*ppK)}4XV5LsfxlU{2f(blgk>YVvLu4 zoD%O8F+{xE$E~x3PKZ9iL}lj1mWDYxB@Z}Q$&{3WzdaV%kK!^- zKtXUxD9=ry-(^DFU^VHWN@HcpxqC1FVDL@@*&Ay6Fq&qc>Hpyl|1H!yqG?2eFSVBg zTPKBudl5nAU{f{G8`l!y`W^R1M zTYG@VKC1kK(Sh$WIvb#reDcW_X#GLPG1cAwEHIl1!K`S--Ar-`8p-4p8$BBMhA@t5 z-k?PxjQZ0joy*ktHxtucV*GG4BouGLZ zVJZC1D94oaeWhn&5a0k-cG()_{P^WRCMK=FYgSewfLX<-w?N0tGuxq$E-k44F~Fe% zzF0@mIzkdepEe{3bx@iIZhX8Y5I}vQU3ry%FI(X~;FT0c+p8-ETBd%19sDt!1^MlU znfoHUc=w78t*s?+&!Ms{`{H#TD*#dird7Txe_ghJkXR1S_v||;_*8Z8FAiMmls-9= z8mvD>f|p~cJb*B0yB7D}GXUGm7?-!nmmKNRyqsX5Bta6fX3-@JQ2rHvEWK z&)VPY{$J)VxSKZ~uO5_P!{?qfeswIxKKOxD?1H$OvvT0- zj>oTN6gi8t5@~hq99r#1Tq9)UP=)TGaX2}3dG{T}5E>D~BSw%Q#N!}AF0E%`m!@w& zqb!}Y+xS6!k7@wG5@F)V_l%++d8O?jy7J@YBxBqrPHC{fDkO^wtvOxFTQ}7qP!{ow z5+I*p6@ACY9oz!-;j9e!K@DE+D!4{T6;})Ny~asvHoWog54wv<6E$ylxrv%Fm-B>* z9eMo*U$M#kn;Wbqo=1?a(4xM#!clx()KieTO*ICGn?X7c_qCSQQ2GbcP^Gu^dmMl` z+;*rmbsn)7e@Dz9%LX{1(QBxmm4moOLlV|{89d8O>S;W13UtUBuaBh_*ij{${Sd1G z<4Sy|->h&~d4p!>TOloj?%N>8!P+;D$Yhrh2+z1fh>z`jSDb{^pdpmmtGAEg-cItr zONK9BGU(CJ;z)GGSnXCpQRHpe@u}sZwG*PY+s`fMn#0mWF$QLsk=|uo6iZplM9ST1 z(gHTrVHidEVyA%B(B+dU0Piom-Y)lY`}?d--7Y7!O<;MOs2Mk;=NP;4(Snrqxg*Rx-kv74CHPQxfOo6}uz}lWG6#Ui+ZWtpblj zzUi%Z6|8@yvXkP-P&colYUin$A5pR49$zR+9_I{3Co14-_%`|-P670U*%>LGGebY> ztNkvfE&c=3in4B*L~a!W`Ih{oTI4oB^UXCjhjc4aSNXUnwnVk}G#QjB+X_JZp7|dE zO~OFe8Px2lE5XRqIpi7~YS}u-W9Q>}aL4^HMn%)`5T8@>ZaL`_<_>{-{53Prx$hX) z`;bszW32|d$@^Yn24hwm7KGs&+jo*6|zER@w1(AUp(b3HZ9zLG_eZ1h13QS>=`z?_mAfKzK&kSXl2# zM)fOCHJdx13hEXbgf>+_vqMS`_n}-~ZsX!r#@Z{DgeMBzu=4cC7+|;RIS+nOqtdr$ zkt&w~GW=FuyR;1ovS=IJZaq@?;PGwJT6Jccpa0Iap8$T{$z68^nB?EwLeaBZ>Kqmq zgVx{gr4}1QT=fWcy-7KvSEHsKPy@&#G*x8MIxET|z^h=f?6`Nok9G<*xhFwdb5y$NYqdoK zAG&@~cw;&8rfS)c%=nYQjXo?(H}Ft~_>g@MLL-l3TizK7=FQ_tQMlvwqi$mU2>bv* z{d?)Q|2FJ_^X+0=UAHr98IAJv>|uQ{>iY%LzJowm%uO{7x)ZO@>+%Jrx zpJA<6x|5g_!YT0CFAe%i!yx$5gE|_z-GCzFG<;8>j*(=j=Y?erfSYYWdfaGMin8fu zisZ-8MtSviy6a8YF!6s_$OvpkwlDN7{pyz|fS<7522$tSFM6zT zJBFL)kTBCmbA~N%iUSh3|HHj1_jjq=Aj%Z~V6me$(PM&nk28J-eItz`&Ic2*muxPdU>%T%ocvJM4hxnAD=?NuZ)yvKuk z(syE-iw&>)%=8D4?x_BQ=LC@YA3W#a+PTu6Fw|`>D;(74IY2Wm#x;>vL~;et8Xh~t z5bJrwq=2q#2p^O-{&lcI1GwQyB?fiDFM7Rr?_~@ntjQXqrJM-cv zg~d)})4F}+meQFn{OdZhgmc`}59+yDa`d+nnRTWek}=b7m#TF=Rt#%3{uHw82{!wK z@d7mN%{E}lA4XKO<9A?1O^ey3OC7P|@c>_Dg-ME>*&YOLEwDH^1h!v8Z>4t&843GW zsQi9oVnw)kt?Rxtp&rWkU&SnP1qx5m8b^U6~#zfxX?n>dYQ8n;lU0>$B za|O|wW2K>zikdsl9|!nD7w3=Z(4}Ku-v+iO9n~zQ#yEOZ)T&pp)>G$xj7#f&1z<`< z?;i^-Ej=jEZ6XTG=92H!z@M=vrV(D@%9=Ov_6V+R z;`s(5-EU?Ju&N-ThtQ&&Nz56ANqw=2NmlCz?lISa)88hAkfNX6*w9%@YYlEyX{598 zxlGtNR5&q>I>9%^6-NS&0p|EtYHo`xeHDdMX8}I{JcheorTkJ0RFISBNBeKzwf`gk zwEyAT>J#U=i9#GJm6gTrOHP=Q<`Lb{oJ@CGdhHH$JD8Fi5!c%Y>hET@Q{eoU+W8YN!n3o(TxX0O@Aa9HJ2T7nqEm~jL75N z`UTLt(e9VX_`5}jUVYyDD^)X7WY1=@6+QxCa7bZJUJhvf2yb)~Uwhx$6<1`_ZV_G~ zVpYSk6aifp0r}laH1;{Ag%GbCqJGMhzj+AFJ!UUbw>GF;!!*L}T*yy_HSu#(`qUX_ z=aXF;n^S4cK${H=^hCJ))PGH!*Z3UuD6$rfTBoI*9O9)&ea2q6*1k^Byp!udd_$_r z2Q}Z3FBxiy-fG#<%zO2c{@9}0DQ>yk$fLmY{+`o$S6-Gy!=WAYz zAfvP={;#$b9Sc)yfY`CGI1_$#kp%I0Lc*Vt-cx7am+XO36f zz(8d?;&HFQn3}9TRl?6GMVs_DOvZGlo6+WrtKF_H6VC$Ve}HxetUE8*xiiC)-D2QL;ExpFD$dtQgo3$pW%!n(j?A$4Pkt*=i$uZ$twmY)Yxr~kZbv8sl*RHh5F{hEb1asptKq|@N9?^-_*@sc~@2;3tebz0DnAz^-B_rCR zc$zl!BrU}QPJEw#Mqj{yMZEjEue1$ zVI)E1^lAb#?qfq)^t(;k(%n4Hh-nX+aYcKn>0}K-3Ee6X3tZ>+4hX(JiL3Abe!j<7 z%($Ye$Ja4+dq_LVheX&}8jAr!N~~|H2heWPwVRygVKaz*@u^6hgmkZpJM~*$d>Z`O zMx1)M8N8dhMjUp0zwz7OX@q}GNefK@3r)NFk3ixYpDRj-yUu4u!}rqZ9{P3I(^q^d z#z|6c7k0htnZVD}S@UL7GaPs$3;&2Szl#eOhJ;r2HGa=)+-l(+w62V@?Zei*pe`z_6-sOp3flWee=9&7sAezC!6N(Mx7nM@lf@4 z>;Fe#-**}!*z4ujn+A){$$^+-g2qMGrVTD)A(W5j`;uS7G#$sY63;*f=Y9P(TI$f> zfhK{8NZ3;C=p`T&_zep1LAj1Yx4gawHFWJZNTPmQ2jZ6bDJ_mpNDMb3ntx((d2K-< z{f-L7_V%4S;oCmlI`b8rdhclRaRTvFfZBe3UD!WpFn? z$@G%##X6NMZeUa%kH}$cM@r32SNKQus_cC=LRJ_VUt>Ce^7_Ph;W;@VKFOW~;uE$~ zdfHa{n=PH!7RDaUwlSp=+XyWQdE%#PIuutF*3AKeyJGT*G!9mRI1o!F$vE1?c;536 zSV&l`sD8NN!_?Dbo^G*BS1Gmp4Jf|CjjQAaVl7d{wJdazM9mOqLNtrOOK_x{a{0E7JF0}aIDEamgTqU^6OVHQ`1iFOodSDi4xHjcCHUTd|T#_e6HPA!|GIa zEOOfWjmt!}R$)9+7xQsD3?$O|f*G23w`Y;6g;uaQqU6AkK%MMo1`wNAvCiz#uVHRP z5ugNnPDL-}ayyk!rlYT(VExU`&CVarEtC5|M!I`&?dnd3Wv${W)p@Po8jKKa$!_W* z%kW+_wW7V5cUD{x%vpQoYvV3)2mxbSxq`hSM5{9iQipG5M10h9VaL)#72|97bI@ENln z9f=!yS8QocjuVedZfoWI2R>c!-h+LF7O0qcIWxcK_JfkruK1y5b<>8PPM)?08x^fr zUe74L(yxOaM0dZry7yn*{y&N7wOhLtlT083`ort|_nAy$2&Jue;tpxT=e$_lf2bQg zRW%EK`!=rPNy}k_p{WznWpNkXS5^=w6=E6+ot*WeC>LDuQT5{=2yk?<%yOZ5`Jp}@ zmTaUL*C3yilQ?Zjwshjb2dN)@#~&%IV9v*g`AFOGw;Q(N>?Z!6{f7PmUXFE%Q8S7J zi{mYIt}u3iwC4-bF-kVmrz5LZ%!itavqawI4Q@(jW)JbN+ML}Jj8{uDQB zhWBYg8)=2y5?l1KI0cG1|7Se<17%g?I4}4o_lX+hX&Bvzp)401LRV7O7SLx>h}vAq zRfu{Yz9$sPh#wI7Z?xz|<_p z=|RdAKFL9xkDl7?XE&+y)>+!_qU#?iUHSs3B=O}bGD72{?meZ3sX%Pj%evg2q zIzO?<*Htp8$$c|?t*Or$Q<^+w>h@1%`L|EOPxc83iQ2{yY?~i%{Ayh7q~UnMVy3D0 z!HXG1sN3-e9;I7V@&o&oR)HSEv7y03;al60|6!6gqOX_a(tf+tlzduJiz-l=y<jbpP~yP_fbbiuIF~bnEHm9h0@8^At??%E8qGL64u7*Ic>x09MQv1RDkgrfL^xX8O>~!N_K5wJHq34};W?c<9 zIV~TN(085<-|8*rQWF_{V?f<2&}*_aD~I zKWH9ZG81fp>Lb4ca(Pqn9_nExpja)hCxM9i`AU5eetoyiKmn1YA9O)lFZ0#W6pye;b65j0bMrR~sADjV|W-#+>qr;75lStdIG%9DBFr^f9vV_(-VSkoVfb+iKAQERQTuT{yGzPW~*gm*h0DTC=NwVf}XUEDfXYE>EV0o@bYkSt1 zi&ZuYh;hOe858wLDrREc@JNr2ONQ zr?t(rp1TmnWc}-PU{3dbO~HJZ3?KPfThH-C)rzZH=M-eO5VbgOrJr|Oll5ye<96=3Oiek(ulvw+~m<$KCh(b$0?A7j@d=2aUw7VnD#Tua<4=1&!QbwfkQbj7VNqoCQFF%*QjWmuAaOYQxmK6 z{!*}zq;N+%XxhH8`oi66hfc%|&b~_{Zxu}!JuM9M_(ns1^0<3u`y5;94)xOD#1*XG zl9YpmSwq+K^IsoTyEJPPKZ6Y2MnMY^^`OWmlP9#snSxv_jHWoT9rTHin0cq?7D`0$^AVE50Ai){Oz zLAd&K-Xp}rrp%7RI&^RHBuH)Q116+Zef;-S2mpVcKsO+rE#t&bVC-Yk6;+RBVGuKd zo;uTbF0~7kZKySWtLNvdng%emctUXJhTSn>2LKz8)^9ufG)`7M0>plhm+6=ypmEOc z2|#0R44nSS@b?6t`GxHOUI^H?Unh4_8Vz}3=7n0i>ER}R&}k_p8Q#P0>DliR{Ywi} z#-!(`xs$5JAz(}+Upum+&H}Hx`j@W)zIjxo%m~=IL;eoXWRz^_pNM47zU#*?@})S3 zpEL%fdPd-Nqj5DS`pY|~_ZsgJ#W9>M&`}$ypV^(Npe4}hL7BfjH|e{(Ljnl8|2yRK zUmmoeT;JIsi!#6lroRHRz-v1H1UrGg$BXxBTcG$OIyUhFuC^3fvex8DE}O1bLTj`f z=-O(nv4qdU+cQZg+Y1i?`;#?+6Geim{^)Q0^PC04_PyXu!4#RAmR@geSf0o>zlGFm z??ZHh)*m9Sn8tYu2oyYeI@9<#y-Pqyu1x!7`_Js?XoqR=Cr-2`ZBkdh+STy=Vnm!! z1%95~(e=O%bgBYqpy^90*53b&B5`KwXZ~9?<2BnaqH-#>WO1*x=~RP{BY4G`RKyU2vamup)(xm&R8D9J?_|q?F zP0OZ`DoI+8&y($vOI}+Po~}F2?o(M0S{=ZXk4)=0Is17@F8gicJI3Fn=)Va194K`7VJw_@_h*FKZ7Z|gXXI`mLt z@A9XUFVE&(x!oJ@4&?i*(FgzkhRE{6>JDbzQ6lDMZiI&@$K`e#y;v(U%}1C)w(Tk= zPm=cGEnP3J1VXbEZJhFm-yLwSxsb3n-)(rhZY&YRYKhKg4GljhmYJa{xu`y;U^JK} zBH^k<7@WRz#Ux&7{El+d`$iW8td(ar=O*96I6&8vfym{r%(!@Fd%?iJBRaWS2U_yT z_#8NJE$r13+qb07RDZ$WKXYSgU!jo?JDv%N=BjtI>zBE?PTrspv)#0*HW&+n5eS3T z#drk8bWeYnC9qQbI@$(d@w&ciMm+egL?qm*y-ZqZlrdHj=;Yh3r-v`u%ebQTp_@&(xlX@4wozqJ z&;p`jJ4X3X4)maGkEYZ>~Ui^)lz$306zN^`BYttQ<{Y&#s4NIBOz z zH+cy9og3I08UiS0U(D#wLEv77M+gL@qg9^rfyMs*s3~aZ*Nd!L`u@ag?ujbSiJoGu zS+)XDG6h}R8Oas$+C^E3$Al!d$M?=jhuDC|JU)r0oe|7lA zL^VgRs}zpZnkTLGFj{g+D?abC{vslQrZQ8X#7{`j+VtCdi+6@$U%DT5TEvTp|LVWe!q@qxq0~7azBcPpHqqAXbaT2*~hc|!xjII=`){uHnm9?wJkN#RFU^$SB_-I{fokRW+ zy=KV=ZhXvGm%;amQ+_QteWd}F^sAZz(!6uq4pYnTm<=I*l}h~~&nT25K4urC{ z5+zn+G*0#I{2f@g)@>Q^^W-1}gHq+v28UC;{1)qr+^5#(V*{Qe%$mbK*;TY7NTdr*zuT7uk(2bV6n#d0#Gl{}5_Jz$ zYIPUha>d>+mGd`H5G!b}Q}$rgncj&(hnG)Dk3w0~4A)E7h-+aXHKwMU^y$yKgHYWw z)c4vul>uY$k2TJnCMA4bFXKfvdMS^AOk>&#D9-$aZOgT6P%z6EU6V%ii~*E?OpIX3 zTYso>GO?5)PboeKGqbNJeo3D{W)Q6rwj+EThj~6Lubo_Js4K9R*kf9CW7KWvwz#~v z^|;%R4E-8Y#K2=Gp3Ah0WX}|mPLFP6_8ulMJ|b=N>J+j!T}4tSWGeis<2Sq0bz)<* zUA;^4n{$m3p$qj`dBydKxkk>uLugWF_%;9qHu9%`(!g_2@U?UBr$cBy>4Nj>+DtE_ zbGhfPa}|Ikcvrn!*`1}|go#fJKbSSkcTO-Dg{IysQELv+2$w3h6tVUwb={V@>d)%n zQ-1qiGJesaPEDQCfF8eV0<(hGCH6d@U*LQ?6c#A1qHBJX>VeJ!);Al7{{K>Vb?So> z7d6Q>rQT9Px=WZ6lJKNp0R7XvgvYSl#>|%SdxD!yI?Csr))BH(tEa3^D`gcWc2o@K zm0`RR4l4zVK8)*di*F>^U4l&7r;D%kS)`JrxO$}J%3NxT{ba`}V3I}%9R8@nC9ctW zsf-)y-*`o;sh{Gy+Z%oJF9Y`D79K0s1P#^d<<2?B$hmkzBr{AR+Ha-*rp)5lO*^!u z-3`i%8g~D-#G>!P{=##Z(*zS36Zo3V%BK?#YN>^m#{pY2_-oh84`c+fsVB!s}%nc06vZYiuIr2 z17O0>e8bcJh}{MLa^7tC>!16G4}cjfDE=ifcCq6t{Hr(M;T!JN-q+_A&baXwp{t*A zqMb(y#q^61<0&Q!aC#dvo3ZBWfXghw)&SMlc^ke%Xvin@-JimK!7so#3-4WNv)u*e z!oPg||4t$q?j`olsq?E2;8VHlI?azrgiW>= zLeBmk1QCqYWJvS(5A(~6R971 zmUokFLB>A+Yxl6fa1B)74%@K26JE8v>E}2Sh?{a3Fugas57G=bUWkSpIE~2Un)I;r zc{!xB7j&cC_1>MRA@`5UBr95(y<7M&39ON@&EB8KGx{HC@&E^daUOgqPx5gTmwn}f zzy14?vut;ShGaJzK75b4>`UO36RWdw+}WRh(y;>YJ#!_1GWqPUWQ^^Y?fpV({=QC{mtB@OaNv@^AbsJ@fLjYJ;GKA#%reB@>`2-W6 z(xosugZ*ahG}O%})5R2-!4+^51vm{TW$9?b>X+Dx-`7NBq%bp6p$p=2V3L;we^Ke0 zt^Tayoe5~Ro3G1ex@6Ir!~;KF+j~>H%$C^reG0R3uG)1&l%Th5h zEYZr^vJ`la(bH&6S zcj>;T9tBmm?C@^JSNih&qBopDjC?btjPGK)%%ht)R*T26_U%D&vA&5c(GOSmb;o1z`t;^1}JwF;0 zNFCnVK;l&IG#lUcAlowfG49t)ct;1#(2|gdH{IPI;-X+3=G&c47MeRR%^oHaJQ?Cq z*NMbhoeZOb3RTHHe7S?)1`-t3ng%+^o?v@W<}Sweo9joS5-t>)*uItzVj?wTb79Tg zPse@Qx2E%M6z9jfkG;EN1WFzEv-=uJemm<7zp}v@7Zj<9UVZJey+OWKi>^x>B}Fzj zbEw5bN%83XFk*S~w(gGlkwCWPrL>;$b$-!`GBU!_5Io-lz|of$ZAnmw$iOx1a$}nX z&A9t$SIOh1+N=>xdV`h*( z2nK*Io}oNv1n#*|S&YU}~=Mrvap@<%kyvPup$Z@YlHZ zw~)K5Hc-3sV3OG(C5HO#*E9CK z%$CfN8lao5hVU8TcYbq{3Kqx>_cLTh zs#3lAktNwf4plA!u5;E=dHY45+%g>a?}O|?B8lxJRW=fjapSx6Y|Dgs$WY5HJrsjZ zr#Ck3TA`L8N4Gqpp$<)uhUnYHn{UCzCo|3-p3XtI2xP1Q3h6cGF^GS%$ePp))HBY` zdtL_T+jV6v&<<9W8JH&@d=`v0*U@k?-dMcRz6l34 zy-`4flPgNb_`&S(9XJj$f)k==vn_vO@H*VuK~(&^fT7O2X)XB;I7-`Pf#WbW0>v70 vz!mDi0&wH+B>rWf-`V*^)BY!3G<}{+1Y50`^QL#s0xwTDUsv{q?bJU34arLE literal 0 HcmV?d00001 diff --git a/docs/static/img/guides/integrate/retrieve-user-roles-2.png b/docs/static/img/guides/integrate/retrieve-user-roles-2.png new file mode 100644 index 0000000000000000000000000000000000000000..15c05408a38f16978041569f3e59ba7d827f5241 GIT binary patch literal 206633 zcmeFZcT`i`yDkg}f>;m~QMyzC=}kI<(t9rnAXRz^y+uI;r6W~DL^^>WJ#-P3BE5y) zix5H&J@75mea`Rh{c-la|K4#KBgx8IYtFgm+n(or=M1kj)a0)cQxfCg;9ONyc%+4c zLkP#gA&?}x2%KrNrF?~hb4AKlR#rn%R+e7F&Dq-4!3qaQ;Z=e@p@DYWt@p-pG15;n zlrPm^Vi0^5f8FRO%|jg-GWz)27ao2yxkV1D&3kqgOs=^qaQXGUTerSVQe0+luQB+< z;jAT%3|R2n4uC8*p1wQiPJ!60)n9lvdMQdFKD6LM4EOz;Eg|?lEjd|zit4!6G(KEz zHNy`zrZ@lo^%0K2%hARpFC;F5WAOupij$F3)9{PYrk@FLUOj&htpB+)bpJYzH@Q9& z7yh$*3!S+)UuWIc`@sAXSEf$kb=f>Z={5hncX|iQ-6#TADjX$t>Zk{oaR!xRaw%NP zKD@M(`l=@LUYh6`C5`#i@v)+KSpVJ1=Ryo&*@qDkbnc}mIVrq_Z8MfvEe=4IkJ&Ud z=-cO1j->iG8dA_hiy(QH?msoILF4#U0Jw>-I9o4?n*dk&VzQ zWzKlz<~I;y{5eSEg6MUz%?HnCu2acS-|q;2t+8<@CCE(Zt4qiSZGw?7$&JGRNp$TFez*0`qW&hgvtk z7F-v*HUB-7!{Pf)o3f8D9lpjrusyzYy=61xF0NZZ;XQ3?>fBGoZZoBe?M85`MCkwYzfOMfYg^0g zO-+-CFSNpQOCbtA{9)kBuV=|EHnU_9^b3dHIe9!R4{2FV-Zftek8KI&CM11EfQL(? zVAhHAM*7-y_g%rACNUp%o@pH3Ry_ad4`wgrn11-a*q|hN-IRWP^I_8l0>_Y>?&iZ2 z6c0zE=ra{V=U9hFT;6!Dl0<|)8zu}?%+$My__VTe8zk%02Gu;1;s)8+)Aw}djr=OD zkM7-Wi;=GxjbY{uyRC4A`Qi;n*9+qmd*2zS2kG=|-aYrb);U)zF>@&+d?UssvPTsm zsL7*#tQ{Zwfog@2!!35xg3E;?Z}}zRw4??N`Vs@DETk=Eo zQD|xtiMs3)c~6E+OK_>w$l_9LfoFkJGkYKCG?~t)@J{oA_0_%O)pU&PuFt2drz2#> zFKE7wmb@7jS-m%e^V8t+P?(2VOP05{heTo#{I0WWskd83)HL16%lCx>ZwS;X)aunt z=L15H^ZUF6%v8x#Z(J@dy}Ycl|9HB8L@rT@diXI0!=kr4A?+^O0NFmp z`J8Lz;6?$G|#yWiA+Ovx;H{apg zd?s+Y1iIigy|RqUEiL6uD9K19Ba=sTwO3x{y7E_ASGn6^++;5v(Yq=T$3Vf9M4PBaTD_S_U+5z-?d6qR3bXRD3!Ps zQo@?w&OjQ1JS+l+<==(%yNIsg=)2r|9|qZM+rRIB$>jO^SJbA^G4Tn`dP^f|9=>dl zL{s=@mI!7`(wyr`L<=v_FPdNUyKrN!Pm;cRMDtOw_rlt@o95SCMO`nsUU22!#4ico z4|^cZ5WAze%S5Lo@Jg1QDIsR)jprL9g(O9ZEE1)5B^ISFr6#3(C7Em=r3Izh_$Usr zo^QcUN}*aMlV5CRoA{Q%7T=bT8~*`Ab?9-*$A=|3`x<2TMB=OCJ>o^%ZQH@^R9042 z{8s7<23F6vs24oikK()8r8G|RKIWU}e$i6Yj?KHTJCflhvyj+M#hzgI7?Yc$bva)- zUvNNs;AnuPKtNYsw~}4mqtY(hu71U8Fv-qRP+2f%6u-i}!mT3Mj$-g|plwBO<-SMM z;eCt}h6O{la)DAHvX`=#@_%qnU&GgT+Pp=z_E?3Ef()qh!yjYdIF#%|mVdhB*-FrJ62Q zFe5mJ%hapHDQ(SaV$YXx=NPl^o9*j|fiDO2<)R3emsW~E=o>2|o`AVt6JN(pT z^n>$@A)zv7rx37v{eYUJn3~z(f2&E}@XBKBx^&Y8SRh3m`&M{UIQF(_)N#^o()o!wsV#5w$D!Fu=8?E3DUQ`cr4w8A2^G4fgEFv-Fq$_P~} zq(dc@+9ot8r0@8u^2!)!>}qAws)m?wtwXI+ZTN0peQo{cz~BI+!WV_^*e|hzjLr}8 z9`Mk-rnaM@eX#OC?m-vC!Np&s-CSoSk34f!J^BZzwb#nGOW*Ec9^u0*DO8nCNibF%>YvdDJuD-m* z__E}M>(HC&JJTX#+j&d!++M{p#TQJVK4RWWzUy0oLf?I_KszdVqy0PlA4nSe*90s# zEH|VE)M8bz4*~}RNwA$*pOZ0cI-g^TNXJA+60REV?6YRvbwiWFl)@X&4a^kGY|U(9 ztToIv9`|6)%BBmaN2gD&SH&#fWtF*wWq2%rF{dm8b zsFuj}5rm2I&aGG zx)0u5C&MF4WFA*6SBz(>WXWgFW*%g!SHGrWk}@vgIWU}J!JTzGi%5%7TQOfTe@vV3 zJ3*u|Kb*Cbvxo_O=#ARcZb?ryf7B zm|=y$Sn+#XoCWs}d6;e6oAN2OL$t+mMpTW((YCHGo6h~4%j3&M{3iFtv2sC|3z>|f6Ari-;ZT> z?e)4Zb;Dtk&eM*Fx?ZTo1WTr>RjQ=pEr*6`W=rLO3SEx_z1g9UMe3Ezj`pXKo5r1< z!Dy?o#BPEy}9&@)})J_QTr~7!nz99D$WpInWKWj_mnE z4^L2pUr5SnEvXB|EPe7;c+f7k5?WG44UKneGirIWOJHn;`4uCz2OFdIA1kz6*lnm!hg8Xh{D>s68dCbEIsSgH09so z|J=y7#lJ-2dOD7mU%;_njis<_^j9QH%$No;iq}>Kiq@*CIPAbN5e_~sB@O{_gbTbR zajE|6SPu6d&V^s+@o;cLZE^7b+@l73p8drDud_D4KQF|+#32N}kpVC74|spwO$h&R z;jd!?N#GjJLv2|_Mc`B0!p+Lc3G&q0{fgvg4dBEj7X?EI4$dv+vlp(S7SjgM|FEr& zfxCgKil~LNBi9p4XLBnqZ%3E2esILSMS(*{EB7b#-i{7V5K(UlhF^Dx0>@{kxf$qx z-QsR9!C;`OK`-m8*W|^5fN@4K5jlfPT&qsh>w%|6K_r@2;=Wg{_5wE6~w~L*2Ue{*@^zF-zVnI z9_|th3}*xV*Wd5wwDPw7?~$A!f2IXYko)WkH!l|t_kZ;bG!;KPE2?4ZZRKG2$kq{% z88C(AB=nz6|Krl%o9aTW++>{{fkE9R|65^y8vp0T zKMlpX&*uIgviL3NUuOYHOA?E7|CiPziOsTaH35pGwtb|o1AGEzcJ_yt4!qs_{Rtf7 z7K(dL-b}^8k;YMc^ianecXfg={^qyI&>5-PM{Od_LC^3>adAo8g77}#v4@d8lK%GM z9)lqt(Y=e0RKnzaVLAvM_-MW+!;7;^MKTm5f>yfmPja`wX`~vv3ZTEGFDKDtD z?xj8}HW&Tj>(V&5`2X@o6eEongu-cq82;OxKxf*|rr%QF{%fDo^dIo>k+=&>vqbdI zaPa=;H+*E>h5z}EUsrL-=@HlNNm0oE`;_Pri!}c`Gta|m@{m5Le(*03cs6{K;QGJK z1Gr6Ent%v?p}qSX>Aw$81ot8Q7uB3~g2yiHLZcDL74`4KOS^Q6{j0Qz=&x(w2BDs9 zL0tbg^c`eB`R~fcdH#&}I*2enJ)H61hrbTmzV`1nL4>dKY+CfgF^Kx#hX+j3>)&nS z|C!|fGs$yY=>J}mr1vDkCm?8(ZPQlhvm{ZRWm05*^5qfPO=@Z}r}wpw&heVrl+S5n zRSQE!8r42?HK0aMUARg0eXq6gSG~meNZ!Lu7s&5;jju=VI?EKtu4<$h%dTqX`~r4v z01%J)yb!*$Fna)T+g^+$6jgVpQE{OGm%gL;<>1c^xMH|uSs2UJavLB2?9`}soy&Rg za*_|4k{g)jwYLVYPsckiN=r`@kTEIH+uc&dq(6E^rWD8Yidl2^(cyQl_fi}->gfZR zWG#9`6q|a`(Qg-^e@T?5Idq55Mlctjf-$MV6I}zUb`$)r&Uiz1A93}Zut@*?Qr>kj zpVy&GhJ9HKm%hG}Io^47YCiDOJ_vPtZ`4Khk4)6)ZA&fV*p#n9uq(z@PCr7nDHbYA zZV^+R`~0gEMAxn*3fO8Xx_mLyM46?F2YxlUaAXv*Zw`oime=LRpRP@$Bg?zuB$DnJ zO(6ocxlHQ5>P$a}-XF(3r|aw4cy{cYI{DgfwZmW=hx@&hE}d-TS%w z9X`bZf#pZ4B!O$IK5|BcC$TPszaRb|F<-izI4>5t+?UOpd$rN~r0|_Y0H0AYMQ__B zI3t<4)bA;|f}~YR-*f8I#?~^}biK1sMLjsFJMcZH%V*3nJq9RI@L{<%W;o*lZW`aa z=}}*He}-SMbX}c*&$69?O)ERrUcdN9wsO*3KAtJnIRF!uj+d{M9h21-*M4TWJ8g5g zvH$_OEkPmo%Wdd4@E$&V7{}p^TkW?5e{Dxsii+)N#X`y2_ps=&2aU--_qli{rr~YxbNE!H}ohb*;RX-;2cAF_RN$Z?v8dy zu0YNel6m(Td}Lt2Jm|`A*Gy)P;j{^3Qj62+N~@>O$jIPDEh4DS@e&-|Mj|Gqc!jG9VIK^FrCwazi4kIm2)|$ZDh)+QHBc*?7eiuEq`7?D}_gu*mtM?sJe};xe`7PhHc|Vp52z3$&1bFT zY=WAeObe{eK|LmdgCFPp4qn%z@e8L9=U?@2{xtp{o(e?3XVqmUL<*{H*Z4z8pFgDl z;*6&`*Lf`HD>1;l3^tPyioc~dn*$Kgaeuf!$2rpctdARHZ~E;tsFyJuu7~yx$0Q3d;MDR?dxa~jg`K%l0K=O_2c@}q_6(G2C z^A-!rcYpho7|`CL1v1NZj{2VeyamX_=}FgLQ=kPpkm&n#;k+5>0}rsd;<|11yTdR; z;5#qM$3^%YpO^jsJi!I#ZjV0?APw;9OXu?H_%kB^SGdGi0XU}F4(RrI&!TxY_KK?u z{~u{`1Jc~b)E!iw^DNI;0d=YKmH$CO>GI3hLGog}6z4H7^AJG)h2ARTOn;&skY0&8 zN$NL{pW~;~Fr21}V)LE94DQ|yK)8HLYA?>w*H^nUgTwA4e17ZSyb2g_H85~o;+z4l z>jG2OWN!2MBf-x$z>Lz{9-PAzu7|w=?swfP*~=V-N?We{^sk_@0arKQ{o;Yn;u1DV@uavMru1^PPyFnp&DD z&10LZ9ky>y6Bz>=DGJ>z^}Mz`Ax~rGX)JmGcpW&G_=0= zQpEM^U&r6qREc9&Uis+`kdsUGci5?f91~u>$@$UQqtSAbYygZMECAiUs+h!8gipZS zQ-ugV|SA(lqu<2wiMri)t)6 zY)I588xkrMH*`CoxYD-Y=Ere}GR;x`uSoF#@ZvPufph%q`96@eqUYY6Zq(l*Ba7>I zji-F&j~?}?_Qkxtb5hVd4A0n2Ze(pM7(~&ue2Z@nuSnCN{DIxi6j{8CO*mjLGUt-_{)MYDr?OXy2xOoLhblJ%dZlx4HW(;YS z^F-p~?+Pm^ryXy`s+>`tg6vgY$`x7e`Li{uAx%^MdvB;k{dKCo#eWgwOjUS(Op;m! znfb!JJZb+-gcIQbz*XF&^#G2^?*aocY)@Kfrq?HKYLCMs$?@yyeW@Tf!j#TQyB+uShT!>*5eO;m#9?q^ z9Ebk3iDLQU=QA%S+Txhsgl;b;e!zcN>bD+P9f+-w97>W|8OUdXHq^Q8w`z*G&gvjS zN7r2!+AH`xkK$^&(gnO1t(g zmZ93sI%$ZmfwL-NxkCM&IHyI6D&LoRiztiW;NXRYnu>*2WZ#&EiTKf}Z=?KMx+eJi z=M7ri=k?N00&0{rTw*{EpqjqBSR*4LXC@z6H{~ynE){Vt-kg$;K4F!*f}J3JvA5i- zNXDv|)tKb*jCgUd(&4}^g~ztWf220t$F!h&n<5|*Q&(&V?o=l~6e={;Cnjf~et=99 z^IF)LVdVRYaEj)Z6kb;>BACp#rwcfyx@wv@8%-gIvLAAaZ~U${aI_kd6G^Gi`!S>= zCjmCpHdX0TkF}vw1bAk|I$K}v6#c<|rdJ33sOZUf_4d}n=O(wg?^nx~H_f9d8E5Pj z+T+d^iEFc+!Z&HHHkBxAJSG_j}`E_dV90#9{j?CXKa9-ehb&Ap6%B7bmnGx63!TR*wF9vM@sl>(<-SL%ZAskG zBjv9Bdi(~JyI!cRlshu8!vqd}W}mIlLhxcP_AMQi6oUqCx$xM4ZJAZm$82kT@+g~* zyYSUq33uJ42lgsb4aj7;QAm={_PpndY`X!^7I;1Lz<#-qU!Frn z{>JW9>zXDEO|J> zr{2oJ-T1)^T<@z|N%^F@W@#W_tG$_I$<-T1cfo)$AEw8}?Gv%2`AJ$5@}gF>EnnMa zeqfn@k;t8V1IAf5eOQ!pHCrJz$XW^$eaQP|P2y<&IOOtTtG{4&okZBl?os-}bYrLo z)X&9HuwZXKj5p}?OGtR!g!ho%Q}`|{fTnh5b3gMEiuzXq`TQo3{$)ubf2V){jH%{P z0O(M3&o=KFDJUqYmF3!RtbCiHtsQ_KJZ96*$(oe%>yk+k^lN~_WU^l`WO){?i1w5d z0wK=cMZ#;6A%)le{iv1GD}bMHrAZEI+tk*ch7BPqO`9cWYh(^exB4y%7Am*JWph}~ z&flql519n!KE5HLWrTLA4|K;zwnV4Ln}%Z9^drn_RF=sWf9`ANswpF*(v?;O@;i>S z0~)CJ>l^f8AMjR;1Yw4{2TmK@Zqw6u!O>46?bVw+o>)*~7CR?n-AREIPW+M*uNpv< zzDtlfotL5zg`J6Mm1eX;2&th5<^~~{CVY4IX7j>ohjUjZZ6Zo(kLR47NZe@hk9NzMRcUIf1={En|zXJGSz4Fuo7#6uvr` zR?Rs}?90v}u2&8&Sf4QxX6>9Si#`JisNJ;nqE|)g$EPxi7HQi^Q&?`QJXY}G5PaC@e3P#$ zVvLJTiz#UhJhmJl;d^z!ULE!QlmbZ01NT;UtFM7WuVBHus}s&^6@!x5)0ouQ}uKiZh&CD1K1sFUrU$7p4YI_O0~Pwlw7fh8h76!XA~lj2(D}9bUGtFbu!i7 z_GV4q4~A+OE6tzpPDj>-$bL6k2yQrPyFV6eug*BGD}VHM^=R*uV~XTpQ2hP>$&?{} z*Yj$iy_t=mI`6h{MhUNcFv>6=l>cs|DOGcPS95CD1CD-P9)Pt)a*Q24e(VC?h&}Er z^<3>P6KVXqQ_3m@SD8kk`RS zj+9!*s*jcT<$9zQfYcUvVLqQ$l^W+nzsH0{eY5P$9LZFo zPQF&#^Wf9=Epqz~k5}j>43KC%3hA10+{I*}Ghd^hyN7nwHy5HE()uU4*@NuKaT@nd zH3wEG9Y;M&xX2g#B{_X9gHd~vwk#x4)aLKUPO!Sc0d{EDx!m@x1pAIa_-Q!G0YDi4YogeyRthiS|`|h365dR2KaZ0G~kuD z#F?B(&+*EQ;4`mmeMQC`-%u}#!n{KkO)){EXx9P0n6I512M2<4fYZS&7qOpS6LF(1KxAh|D;ad@O0BHMcgBCq?1~Re86{x@KoY{ zmGhKPv7>49;kv7~<+p;0!}knj-z(lGf{F;89P1DLIc>V$mlZsbL~Do8Nd;=1h5M1v zY0)d>SAjJ@LZC`A_gQwH&K5xqnEU`9rxBoG(+JxSi57GmG$4YTx3V+QLInp!thzI2(4SJ zl#i5}^=g%^9!3Y?m)N^Z)QwPSAvAEeW9t1#T1jL>t7)Bv(3u5{H$YNALakG**Cw9i z8z>7sX}(ef=FS<)r0rS1>?rYM*4?niKVj`w%Cr%ULlv>J4q|1jLfd$bkc3TFb9Z%w zTQ=};;{Mnavm7Xu={Oh%&-F~j*Tf$+oVR3fo!~4ZV7SGeY-&{J;jC)o(F$+}%g@MQ z=7HOsXj=}$kI8i>MiTX79-Q`mKkEXHxDKp_(`$JT6lf#J0`WS0is^!a4H|!vDTmY1}JopG<>4yPa3}W z<(P{`GFHG=I~6?xHVxTl@2C2g^s&iFtw=sE)(O5kCEOQi7_!s6FE|=COmVxriJr(2J++ z)*UyaBJQcav?}bVn@kwJ!OQ(b$SfcX}gzmD5BbK%FdhH*bPqX^vxc ztcGRQLrbF2wIBzcz*d(oqMq%=PR{5%Qd&gBHS5_!y!xmVW2&YK<|bAkyJiNmYs8Mg z0oQpTFb{x0K)Lc;f&6Nrii(S_k3wNCkii0vsEz)>h_{r7o!^T^zStiH!V0^#k;96m z)aTE zgRc&+Ra}a$Ih>Bj$MIVDZd1QM(QK-rRCJCc)8CtHzMbaiDEPjJVeoyvr7mVE=|ZD0 zRO7mdOPY|X-GN4=^y%s0;Gvmth%OA7%D929C4!?V<4L9I;Xrwhmpt#uOQI+|gNs-LfwlKYD$jp-_qp%AykR0SU zpV!!_IT3Xs^0frQX(~Ty9O2qr5pGzu2LWFmYdG01U4U5~f3=N*3`e5}DxyL4zRm%T ze!02t?=xFPo?0rTcfDINBKW)%(c2oH1m_7h6XdKJ9Vl6$h8rK`rpHs*Nl>wT)_=G+ z_1%5k?$E=Ya@_+Z+?JlXu3mQU=;kWnW z(+pHGYSnO|-Ya#P%dW;ruwZm_(r#3Pm?H4&W?^AP9$NcVZB%k)RPm7(r>4nJ&6Hm4 zp-$$fL03Bg1j^bJ<=_1t%Qv4p1+GoTAXkurITFzE`|A^a(t)={I~=7bfm;0YWB^cT zZoJ3a`)ASXw<+P9JexkmO@CNw+19a~US{288O1IMSuCX9--{8Ms34XNm;f#@5o}4_ zwhwa8J{DFP%weYfqO zQSjs#>Q5cK;2k`EXyNOQ^r#AuWFtJo&5|=?NR;4l`n_oV&-f9>0(i^Qph%KjU2SKZ z))+OXNuq?5vZc0JW{&YdG(2r|Wx3=I?&>doB~3pGY@2BrlkYxvPZKo|$gf>vZy*u? z_WOz-KE1mgW1LadQ2|>A77rhM+ZKc=C$y`hD*vz|_q1P#Cw+Xj<}%ft?)=xH%in$1 z0O&UnKV9+-JP}PxD%P}G9y2Zb}P@ z1x&M5(*DHoRKdWV4>LJD*S^=rOh-#hNT-*?*Pv z=A_+L0k9<5vsw|bWMWw7UNILYcNL|u6&fcp%AlPh?g&5jI<`cF<|xF1R**1eg3IFf z4f$+hvJ~I60=iq+f2GZ5-(_xC?JJ9DsU1s^G7Zxy+KHdUEG4tKE%#Z0_jD8z@8{R; zAM?ktc(9lK0L-ZkzzXV#8!RgKI#U}~o^DspE@Mk4XA)q-?cY1c>Lt=fxb?dnw6;f- ztPjQy!uhPn1=6GTav=F+(QS}H4u#6X6lU4Oa-deHx_z)ZT4wrPE%`157ieoypeEYQ zY0DQ3_TtQ2YV<*=Z9DgR4BbUGWGhLsFo#R6jIKf^CZJwSig5;$4m+ii$i4-8E7a~p z&6?xg0_=bX7TbkSNO-5~aW-fqPOHv!QA7JPm@l=??4l?d1a z=Yi8RH_Duzul2A%4^FQb$8v3ZaNsno-gyf5j-*=z5W_e-0xADXx0m2Bo%_ZfnN`6SqtaQ=V>z!r}Xn#FO)p`;f>LKy*O9oh(`C$;gb(IHI zWxFzF%9Gms)CAa{p!a24XJ*S;_E1q_je6$2pn!nR_lR^ZbnOmFReFF;D#>JAwA9m_ zRJ*}|A6H~Nf>UK`%JZ$--j&3$>k`-RwV28&7IPD#YS`lgKPE@eVS+O|$J(be7_-(t z)6qY=!z#GW#FrKU)W;)rBr;5GX*+a~O@d zDga^H>RZs!p2$|QqGWw&@50i$mmC(y#w`8XcM|_NQUAi%zRs*_DV&ay>$V*@3!ur? zm1Y39|3>ri^#Gs!bmF?QVmF{3MLoh~@$!lrW>i6ur*7L;R}<}7m*9F!KC0* z+L8&w{cqBQ$aLsdRJq;3%WK`ujbna(>S>~k#q}dbp6E%sCtjw(_T(LK5RVC;wMucf zgnbtSV+8sGO0S8Q(#UZ#P!8ZI+j-1pO1j={D9w&uQqa=XHnz-AsC~qzu6eZn4RVu$ z*weIHDESO;13q%7Z6Iti(DW)f#scr?){S7)&s|6WVdi$)!4nOyVw)(0|3X0>#!1Yu z!L1%ks`-!7-SgXLAa#Kjuj)5QN&AzBku-U%46s@KSV?*EMV=+H2^4xbAAUULw(N?Z zOtuz}b<)dplyJlnVwrBRoMw8#G&}phpR%(f+$VH+d;k_u{ZwT+Z6Z0&DAM1vo{&5$ zP}mZ&L$_0gnu)3@Zo<5?g(hn!7_*L)dL#`L`a!=|4#FkDvd#TAU2T`<N!dIvH7c1x4kQ>BF^O3_1~f=3=MMS&VU@Xj%^n>TMR#sSHHB4bOGazT6>GbHCq z)ZXWn!uxiO;|$|$v*2KRLC(6vE<5dRaqX8oad%~n6oHzVhY|LmooZc7E(>OhHm>lN zU9X}T@cIi=&QwyR1{51j zCL}fi;``c2{lQ00(O^^*tP9Ilo!uabY^+MGXxWf(cs@ulT>HTqqhcI$eWtXB}&2Q@7-K%)$ozCwi@m5f_n- z*v%?;v`FjZxBDW2;Q}(4h-`Cp-Sj*+vkAcG)^RI0FTD1Mo zm>}J89pDIex}Vqn*%N0%{55pix8-moj^VM}_Q9H%i$bY&8xsi@dUa?+u4B}cD9E1F z%N{fq3HlKWWGeE`1)7UAk<#u1!))**mw`ck+5?onR_T6b4R*EqvbA?U6^MB9BC@_F zkRj5v7#7{&fABgUZlKR~C$mAq5uWkHodCIV=nU=Av_3BD=`Tt~Yu*7B8wvFC%J=|q z)%VF3MTCqfKAJ7#`{3o*LbR$bwiC-cms4Hup)yNj>3m$+-dq2rU$eE*D(azK3KSp$*( z;dED?BWOYT`38C~PsOFBP)_xY34GbF0cd7_>uAU1(%UD<0Afv^9+hhH`10bC6>8Mb z_eHg@!g}CzZVv5kf8y<{@2=7toXeF8wuc^8Hw1e=%k#B)U#$7~ zrGLL{x{)f_S#=3c#rB_O@N1P#x{wsG1;OaoRKK?w(63$kikbFZL8LEiwydd&6v?eE z`jy9(Oxkg!NX@BZd7yM6CZb-aO2`RvA~^O2s8$U{+pE*J_I@xkfs8|#U;B(^D)JoL0A3NgZOF-i=IuMZH>l8?7o0ysu-2vCg z&}i=cbP9hxZKV36!5M|8DaG@t5-7cJ*bg~Y7MKKdC2yqO4A>!^@H);i>^m$wn#rwb zMoyot41dRRmR}RjeWl<3O}_kq1F>6`j-`o!d&$)YH=eja3l7hq77F|pkW}OdNKyV5 z{!SG3+0R*+uNY>1#R9?&)WRl1^!|{bjbLfeH8`-E!e*@3J&-Xsn+;pQSkt8z*e9yQ z0eXIGC5VP(kPS8uvLJTVUFzLt@FDC^P*3tO(YoGf)Gm^4N~xMvd8K*4_nX~>ITBc! zGLnE`nhD8;j8cXBIgA6)85wrrE7PJ`8U%**-X8(jXsXE);jDa1J2r8PyvbuwNxf}d z3WLnu^8kKxC|$v_vvmMPHFg33ptxDuZCs1VEpDnXn)6>zBsknBhY7X9A}5g^>I6^% z3j8Cy&_XEHsrh8&hVuPxxfFK14!IH$e_BVd|K)f8ZJO@ zezD(E51AgVzg+Z(R7?{-M^J-mqb7^6ZkI<82a^gTB$JK|=>g+fmxUci^cFcma1@*% z2=$W$t}E^D(qc|yZPMVTOH5w8czCa_K-eEcu#7BATdOtORI(jEz5%WRYB}~5*Vbo8 zEFMKpR|pACAl`b8k9E>g7Fc`~MRZ|W`xkxD>^POt&(?Y>8Y6B?uyr`fJ$8}UM>%cD z6xr-l4+@OiiLSLer0RFal-p{dWG_pes3dzt2V8}x2jm1DZ^_aHbcGEWSBf6~F$KqnT}W?4^I(=N;@xlDYrwHMh~Cyk!!_XrKp zDJ;&kXN{m`02sUV`#$1MP`7h2OcTLzE23Z3E)7&+;$&HQd>}*J-?TpArNNH$mgvX_ z1*6gfsW^pYofX4#FpncAD}>1LVba4%XF{4=>@a0nIeuR`_FZRpMzz{eDt)4Cu`Hou zxua;iApc9cPlIN1?20BSXe@V9AL%!faLYtB)oCIX3mKZ9%2voYuF2P%sIuH1N~?hr z1HiWuNzxQkz(4l*17<}+T&?hk(j8kX*@naTF$lH zys@zBxYs`%DiU+?@7cpgjnCkxj%n{VM&l5tiY@_DL1ceP(2yQ}5ea}S8vwR>f)kN- z$A0sBTL5@j(~*C=$qAO_0wJ=ZODJZQTpUl;8WJUgWPFR!>pPD5g6~r6{h$No zlQUK`J@9Pbo!vHVn=H#}iZ9g5l%UXMGl7n`WeP@h$fgOn%h+nh69kQ;n3UZZm__BJ zWyr1ZsZ_cILQmpwcVES%`1Ipi;p80If}Pk9ChGOHZ`(UwvN*BO2DSHJ$bsGP#x-lo zO&&X&wR9edw){(CYBP>5X~Imo$3i>GKEno$lcQZ}jr7q{2X!rijYWYg{iaP7jo9OZ zE_d703<2lEf|Jy;qk>Xjb?Z*U`v6^MM#8~f6RI?Sx3;IGMBL?-TngTfisMI22v1*) zzqav65q}+=9+fQB4Dk<9mZY+7H$r~c*^8aahU^uOuiOC1pX$uO8AIhy1QkcUHXY;3 zurt_N$SYb+Xgd*H`4JyNub)=x$h@D2F&T|?)2|GDxwcztZ;#ARkDwI$Ho;Yr-YtmG zBfOt1sI;}vSriQ}3oILO+Gd`5eY2O#B;)IR_N{f6KS^m>ldA@(EYDq6$Y0R^42O%L6_jVF<#h1)vBppu*If% zhzGlL1797*>wKK@Zrp=&WyZ4tnvah6guL znW(5qvFA?8H)xp)1DKrNr_h%LhLJ&%khQuZ`C)TH#yrzXH}EixTsYQ$Eyx}-S!^iY z<~ntu5&QUJfK-RLDX>5XpFtcxYz20qPfR*LifoP`?d#Uz%B`(7wwV+}45c-*<;B*g z)dVn~;guTq64x*9yF4J4DQ;ZhFKZ{E|83v(;VsrmO>L=*KBZa`n*4V^{oa)O-vzhp zG9b6=Gkyn`@JnB`;GH$IEp}^6uVgO1HSRje*3_7%jGi#AFK=wJX2 z4n8EQq3eJNnF<{pi~~#(WVdou!r0I+HL8JK5p=MEr_qo%Rq*Ln4lORPe;cKnT>3Zy zef4&BY_wp&U!o(=(=^Cl5YPXxFl0;7Zz}iWuD%P}xiNX8%d#_0vBq;>V<3X~5*gDc z+N$bZZ%b(MM|GLMBPFgd-b~04u%oySq7eB1N~dims1D->70VU=O>3A`zh8$}CqQ44V&5&}v<2j#MKFv=Ml|4VT4 z7nICJa>_7ORknAPWf}e}3=eEBlKS#nDw><0Vg`vJ_5YMzZjB#X~q+(PLh#;4doV4X4vj466de z*C30lJ|lG=xrKtGCt`tGRYyiH*rOA_SryxUcrG$mQ#-NPfJbd_PoT5D5xctnA(u`p zFEy&m>$^<8UiQ0V(X8~7b>oVQ2Nv1(Mzsfj-^)p_gkN{5sOU1Oi7{E1dXRlCq2OP1 zEK?eE8P1{WttEHN$I}~w4UENz(34H;su+w}}r zZ^EqRH)r=S9tcF%dPHNoT!=X$di!?SUJtga31SeaTflnkVZpTXczUqMaL(w<6Hz7K zU8=&@Gfulb8Xi0PE=6!bpH+3O*-eImr%rx*H1V-e)xha#qav`l*C-LHeAX82GSMXa zFeLe`jx!0f#?>gpML5A3;n1Tw=jBmIjC~a${OAw;xWbLld)ABT8m8;|Q)ubP30rFk zq_+AycGi(@Fqc<$$)CZ&DHEc>`QfEMh;e-XIuhVba-{*_#s1uGxqFoFcntb-6pN0o zQt_(Q?CcvMi@v;Ne#4}^7-6kjlnEQ!)6Fzw933c^C4bLVRGVLsIQaI*q|+uF!t?3@ zw`n!|<103TSIeu$P#Dwr1_{aji9r#7wdJ68WgazEtQ;w&QEurrs_iz<#Dl`@=B8?mPyq#Xr!OM#`)l2pCU>*PB|sl=PQ$?HEV zeK1#Arrza3zE(^d-8ty*KDj1$^QQcgm_7Oi0Y|wfJ%HX6btM*8Pc;OG{=A)2w57hR-Qyroh7Oz z{&yxMJ-&29Tod&nsz`ZbMy`$w@a_Lg@{&uGoP z8DIsE61m5G?++?Gnnw5` z%{*Z8tL!NCKY2idUqG)A*>Msu?=3F{`zTXoI|ucMP{aBR-(H?vGKPigr*Cp1^z4naUR|+0s@TwxLyn19|P`UqO zn770?cc27aYT0!g%LHUdce|1HaG>N#Y%68-!R@dA1yERXs5A4EL@F}=+Yh}|?NAD~ zcvF)CFxW`Q{)4R3;5x7&e9K_@ZNbm}TT?;yA^k8EYu~=d(mfFQ&FI?_nu+&KIXLu+ zlBk58-fEk|JUZ9K68o2A#Gd68>;CIx8IB9*< zd2ch6S5{{IL@F7YK2+v^RP?eBuBwb}$n(qBt1Vd_t)E)?@(+V1VKNIr#$GKd3Lk4m z?cX-yAPllcIZogBD-3d7*9WjXr0Q+^*yvd%)Hzw**i|w}JGu0lV2)XS_r5g+c_ENW zG^2M~V7FA`0YVz$RlsvJFwj?gCzEvWo~&ur{%sIPrc;{HkJLyL&xpFpF*a?lelwV) z-9Rp7Mm@01a&-4ed&5N}dzkLJVot;|rg{iI#o$3%m-$!i8a^m7ooYH?dv`B4S-)kh zdr6t-4mCBC{Vnylh)Tb}o@t{A^%MWQTgVM(np*`EqhI_aS58_YQn}EgdzbfGm|X$t zEqAgJzUIZ^te}=6Iis)nunv_IXz(#mdM9Snf&kgr4?s@bF8HvPR^}gB10mV|ez0|^ z%O-oeyiu)*P6&`x#m5#m9(8)?*X7*+wS>2HB>e_idTZjZ3Dctuo&|AmliF7;%b^ky zuM#^=-gKom7JHFU@pio<1_>WGevnB%sh3RBM-Bde?7e3|Q)$#SIyx$fBiK+u0TmGe z0Y!>*6{U!PNRuuiC?JFuS_lylu>sPR7LeXT4ZR2|y+a5sfYK8}XbF%IxK9-C_l~3A z{qCRp>;9RUOh|IhK2OSIo#k!A391de*6;Davn2veg z(+e>_xyvsqka zj|AYKnsO>q0U&%DblDQOcUtt`6=!;kgmYB|pkL)|$eRQu|JN>|6M#CL^g838S)A-{ z5CrN-tlDx~6+on#_vRGN&j-j!X8vV`%jfr0$7`pysqC@Hu-1$=EI>no+Rx~>%?9q* zChKcY#>qQt)M-Ysoqe+GBCkmV3>engyXW_S_N0+frE6%9ZC_Kj-E(W?1>+6ehsIa2 zeLZJ~F$jpzB}9U8zbil?%u*0Lw=ARbRsbX6Ivt~KT#cv&@HTbKmV6YqK5Z+WJxp)R}|Dyga92?({!of8icOo_BuEkbqir45ax zW6i=P8vz-c@;VBmYxCXqbveU48#Pb&vVIp<{j0Q{PXujY?!3~&Tc!WyouH;CuIp{& z@Pb}tQw0Ah_U_d1WWnA>&_Jbbp8ZzCh6&mW7@Ke38utSt{W!p@9Hs8Pd7uENK9*gj zeQbwly#ZAp+9rXXz+3*t#to?km4Wc>Hb8(da+m@`#Sl(W48arAcJy6 zTU#Xlc>N2w2wp-MT*>ec+*@CGy%e-l!$O^|{hID=eg32#gB#FPU#GsMN|j^~`0AX; zjs64QZe>WUKyyWpe|q9)&p((!0ubY5`jhg=@g}2MCOkC>#6;( zJ9!}t$PLpW6BS#z*!~1?3(u~*{I9?G_kVq3s2eNXTfDZ^uJ;*g*A_3~p+Bk{A%J*= zcnrRrkb}RbQfISN2>GBNBHlH*?;9^3WMtt##aO1dW5Wu z&wo{%65Nt&{*r0_wcQ?s92ej5lg7LOP;yGpCH?<&thVBHzYN^pEyT5z~Ls)P_ylaH2*)Bv-`6ixWhi`cnRc3fW9$W@+aJ1 zc;(v297yk_M zuUEE%!R7pG+W4n1b8ZiC>p1&sB>!*318Bi)hyUKX%H8ikd_GQ>UijlA2H~*i85_0Q zn#5*cz%39wmhAp;`P($XAH+R)^izKO|LeVfon(Lgf2a56zIzhr4@3CQM4kX_X9);y z1V1IWrK)l&MuVhTSyKKDO>fw4n#qCAJD5_{BEsY7rbcT~hAr1nc3OLb@@ zj4z0$cr)T$3XGdZDhU%_$wJjnT&t9BS8FcjP#cylJ)<5acJOf66$D_lkea@zMOp&B z7CZ3v1qPG3A!TY1Z!9wGeh?5R>`{8;hh!BgTkk{wV6mFkKy zV~K9oj3oVO#=A47lTSGFE2Oag*{7yoNmX!K(Ss(f%j+7ZbGTfaxbdcN#g!_jiHz|k zePisH&DW_@W~#VI8xJy>!HI%X5jphT7 z6gos&_a=P36G$}kLfD|+31s^t87t#r_eA-xc;e4_Hp~!l-@DotD0$t|h;YB>`x6tpz1;&h4l%Y%pk4`m`g3=l}S|LFa47mCgzp z;21U*n~_~%;n9+irfj2PULeT}Iax!u{*>KjgHYKBar(6Srpa!u#qrMi!Ut*?XeU8v z2aIiRf-vv@KV0YgqVp|VPkSs)7D>LJ;Clm2k#Sr7+*t)=+twqcsfp^k!e&*ck4xI^ z>*jj@d{shsHDpaY*&$&&OGbDmR<$64yYc0r)D{XuN1emPIni2Yj`g!&>}3+Qt9x9( zz-qW4@s?7AWS(-Ks8FEO+Du>%fQ*aPGy406{i`#?f3k>^1K^qPsAlz6>Lun82Z0J~ zB3VDGFS=G}s_DH6m(ZQtT%OaX4Duf*v)unAb50s4D%K-l8HHyw7htKZSzRExl_t*qay2Wsa_AqNHC5FuP;8yH~J8=R= zO&L+h#QIxrSB#3?3QP5zj@v$ugy)An9;s3TlWwj|<`!=7%D|wvx$vlkV%;0|q?=!y zCM!vOrS++aicV8uqRBTK^4V<18#*oIIc=eOq@XB?%G)*TPV~Bc{fJSs{&G@0U>Ual zDEvmjhBCmjPv$=X_%U~V+4f#*{V%YHIdmE4H-13{6#Q)@wWPja1ovi$TFx1rp%IoT&RSdY#aR z-WOFaGx-+C3^8jRvt7%?ltB@bs`DP)9@pfpaW@SsaXMW|s@W^JN|=5i-R+vG2T`aW z4a>1#Day5NW^R0YDmJk^RjY@=byx7F4gZ5yR8(x`uYK4O#fK~+;4ho%>h#IjFN-Mk z8Gg)V?xkVqaWZb8k51r21jMEd>VA#(sZ28W^#nv6S3@VZv`jlv6+Pdxbyp5GJo|RH ztkqc5&JZ~xy@IR_Egn>}etI?m!NPqSgG|pNWMWZ~1JL0M)wW+2HQ5!sR8m&bX?M%L z6Dx+|-ve_a-QvN$0*^M9g1D0_7Eotyxo^q=^+H+8`op-w3&RTHaF3?L{LALC<>X*) z1P6+cF>l|T(uE>21$t9h_%z}|fX?kKL+~_wiyfpp4j`41xt#W=ZFqsfHVX-o7p;MU z<+UkjWX0{7-<)txZMPOe{cSJOHAKd3?sD0)qsCM1k@{e9}QgM^m>uF)Wl1 zp>j5mZgP{OJJ!EA{bWIBG^z3F4K+@j{3DEU)%1{Far#F%wWW0NK5@z=+V<+s46+$S zcsgJgc}fDjjl3}G0u^5+qmzMI8#2L?)MFC zqvK4ROv}s#kq9s)3mVT_FZBV*Wt_Z6n5;orQdOIeum73L=Tw~7dA`Wm>F1b+p8nG8 z&n}mvq|_cmjZD%A@9Itfa6XIka-m$$hSTUu7v!TG_1(?_z9x$sdkLQIelwS*RK~1G z{#ZXaSc-xe3+uwCssGj=TbY;y$X|{>7r9~POUSp3dutuKu{?25)S}n;(&ZxVasCnw zq;U4*yK*D%=4hv2Zc=b+R+xK!hQ)Gr>V-bX(M>^t>mmk0bi322b#2r%;O|Gy#22H#_CB{-y`Y)3TW7DW-COR4@I| z9a)eX6yV@e0YfoAO-yPvN?!xD>&Y%+W<&fswUq)Q#gN1 z&sRN%oD^M1Z&E^kJG5*b00r-+bZj4F9>5MS!_el=OUKNVjg2I805u1S(d1ANaG&wx zFi-K)Z&x_8Ne>=~^WKr`iaQIq$fZ(tRA)?P$v!f%1|2mZ`!MivuU!KJsaA|MopeT2 z+|}|RVx%XdX3BiEZu4|zADE=LHZhW%|D7S_6GqxT4jWZ-pu4_4+f`ofJf6v{i^9RMK)8nZJun2#2gy+aE0oHv;>uz=5u1yV3| zkH-9$o?jlo_ad~92$`Y=d&Iu6HAP~eY>yR8GhUsL@@#p$1bCY#U5B6YT5*%%MG#|~ zEZ$gCq%xav&P3l#LI~J&3}hkszDN?5Z1fU~M=TPX|R}6}C6n-8x?7 z`o*}OI*3|9TXc&uTvw_fy2ma*da*nwsZyLiGq(9&;_NMVJ64qDiQ&n2R&c=lzmsmr zSfar>(oyYbpOfK##O zk(_5;;MGtVyPVb2F}%R6E>D4WlFHm8R{q;}giXpG7nSb91_o~Cq=}~@QO1KOt`f^O zR}->B&S{;-&2kK7aDuHj1TKS}me1%wDY^lrS!H?~ZYNeCw@7Kf5fg=gF^H0ceFEEX ztD*Oq(9|}kLJuP?TkRDB?n{LNB(T(BAw#)ESh(YbbMEgnM$yWt6OGcTc*S4lFZiTBctY-jtCu7~jJ`X+xr6u%?wwsFw2TKAa3q-45I&D!70 zFo#Mvnt*DwL;wc2u5)S)g@XljuBo-GcmkzxApJXg|KFw1e;|Wzks!rBgDeI_;ymAI z0Pvil-n1!%7QL-^!I?T7rAXLN;#%HGVP_`+LchIHHc0jqFc{234pcBfMJVdoK9!HZ z*uX*!yz?l|fkozEEl))v%!^9*qRL$ee7=bFR>zidq3E0K_QY5_QgL|RSnA~guk6FH ztQUnBwl4^TH>;(*VXu9|QiOzjsuNng6%g5aR{TR{hT|K!wsumvbU44sHGs$`mE)@5 zMxqnvvIxVwx`rvQf%?_HiOfHy{OtXQL1q+2jO45hUdp=_Bm2qmtedSs_3@oGX!>bjLn487^DeMf33$VVWD^FLvl3!?u}qc zBd_V{9D$&tFrCR(4$`AyCM^3K^haz?)Re2bYyH*DoW~=1gC_!VhG7z*6#u9TQaasd z(toPL6~517E#lZDb&XM?6T3s%XwN_Jjto*Cni>dpWwCA2hzU`C=*Ne1j?^=wkmpVAq-b(Y^hquuJrHxxYT3*tTa#~j4r(EaNEVot$mQ&|JA{)dlZ}uya|8pwk0;FT` zf)_vapfPuUwJMMyW@zT5Xtk|5^mW!`D3U;pL7WVLn(5A4Wz6GX6jk6_Bok;<;{LuT zbL3X>L_?gqM7BvhA;SR(7uO!53R*kAZiZ(o%^x4;z(NVt{I8FQxSgQJ*N`C!a2}L) zMCMEbrf@v2=*vRM6wZqOQr0@5P1eT@VoD!kpp>>t} zk}8Q)fP>l_+V&1tJUW2W`8w!28^Ww>x_c>H0c6e6g|vpwISykZnWgbYq3jU|ziwuj zWU4*`0Y?kAOf~NtzCSR3K|t*Pct@*rY~9XKMvmPK7=Fxq$*R@4Fuf_xPLS86tT2%B zb~`Wh5#Byy-V|OPadm`#hRHQM{4jrrOkg(ESgyasI@8{nlTH+_&^a}=B%aX-#5l|6 zEa2M{N9{jDfok05GkN-eA~7<;7K>8*T91qXE+$d~y1c+T02E0={MtzlvzfQaJNt{^ zk?YH!t~l=~hEH{eBO^IwR{El^2WV)7o<)~ZRsgMslcw|KdttNzT552lY(7rxjwL!h zc<_dr&S5Nn#R~DRo%bfJa*j^mFwXpFD8(jdcz2f<-4}RC>K)|0c@7j3V%P>fBv7K~ zBd=9m9PXT}WRf2Eo^}6kJ+m3WZV1Uoqy{?0VwXf_g2l!nbBs#`N*CEASIVQU*55jAe6!5JkGa{K3bS8I!9sP@ObNPx z^$}`Mb6(vKawxU}RBh{CC6yuIFjO5>!otiPa>&2f|2@@69X*`8Ad}y?yEj`1bWB9? zK?!%#?s;dbj#L{WsX&Rl$VkUs=KreSup4yaX#aWxmy*J(ulby$Z$yX;|?N-^}$ov7oEY}-PNu3>!T4o$LQW85L7+Nuwb%7szz# zzUX8pG}TIrzUDYa6^(~T`aA!C)uOvf%Druk5_(-$rr$<*?2myfolmlR%yd1?o8$)Q+3*?vZQ zzLUEyIe6bYW8TaUZxiNm?Nc9yQXd7qGe)oL<<(Doxx#(euZ^N!Z{|iut%s8d3(S>QO+T*wCx%I_DW*l+`z@-^`C%u z7EnR3oS5T&cBVuR?cJAK^bhB10FCv+&5bg=X1dAdA%9rhQRAZ`dMe%<)Y}D-l zWth1MGif93io0YJ^s4Iz|W22@QZ>tNM;?XyqzKvTG$}&fX%ck!u z`<3~rReZf5>g}+})wTMmO!(&Op=yNLorf)%Ii*Iz6;Ew3X_l`MZ|~oT3+!-P+kQ1h z3~(1b7J@D%=`?=08s-8w3;Zc0RD)FBe@n@S|}hF8v~1N!EfM^P>rhc~juA&oA;iCq4SQon=KmcEh*O zdc*{&&GQjqByW~~jBr>1qD@e!rqq?|O=maxKXk~BOI2FSpkzQ(aX1!ksk0Eh3|{>l z6IPdIWu;$`$&jWt2X0#D+(JSD|oNNkHtLzNCaRH)tc>V#v09D z{qH0yte1*dx_t))hYIbJ6-T|sCe<6mDswAeB&kK$y%Y5cSG9Wqh1ub9 zvnrcGj5!ekgSSS*&&ZV(vAX1NIK~ROIcnJkWwypTwRJ}#0WpX9FdXMO^kJ<#*(LoU zAttlxR$Q-RwvDd)z<6hwLiX{1*raq5|K{wfRh@!mN4};%bw7@?>V1YGi;>yXf z%S!QDwAeCo_`;#;;taj7%(P_<3dh)WOKX5JcP=bh*h0%hwR z)U2R%F0NqZ2}(IzW}`x#oWLCRnnN4HecAo4w`A{WH}Tp*oKri@^lnZhqVi4JTLUsF z;ghoOI!ZU=u4#%UJ7K=0-LNopv(;?ep-omLR60D3`@uP7GS3S*jkU4Oq?|KFU$S|T z!3ety@MoFV4sZUoxc;xT_PPdGW*hoR70@$>`&O~cjerD4o@C{cefvdCzljftE^14U zU2{IG*00Z*dQe2CLiTE*??GVQJjxFysHW8}&IeZ@QwlQKi!M?c0-VCW@1;U#w4`!W1r8i%vL~1Ti{NAGI0HqmU%7Ul)Zf zsPkhn$b;eKn~&tS+wV=A8AZfg`a)c^sr5AHoV2n+WSFzxtn1cs`BOBpLVI$R4|$l6hVmJ zmABi&1tKE3Oa^7L+=#}jldVQwnIpH~i+Ma>;tl+Nc|GbUJ(nMa@H(Gu43^7$(DGIT zaKr>;+=#()zz-%-IjB}o%8juj*n?Xv-*e;QpW@UG;zY0T_1J0?R~UfuA{ypj^ryl9 zl@F}S-o3IvX_GNv6^gxb`A^NE)88#siSDLb&fpLrl%qU1jR!6||K7~EUS9dkXoh%k zgF^57LphxdqMJnYH2u@j6%ODFT}MZMHlu18roXaik0bvxQTQJ}HChSA&G4XP;SW3R zZPkqC7EO3^=O5s;ZN=dJaQ8p_q%KtK29x1v!p+z}T>ldiC^TrM$j8GUnP)wuePF*`2><`|M7%JHY!Td|*U z!L>J&w=Q^;VXP-}Gwe^-v(B&++oZjd{P9FT+Jf;>S!Da@*dGGA9|Z#XwW;;f&xt2| z#la4HkNaog^-E9lMFsft;9`=8^Vah<2G6Uu{K>BW@r?d8Fie4&Fp&*+h3CijzldRc zzF@?|_D=(vF@O!8S!}(%TN2#9L*nGsKRqvfFy3I$m5$3>&&vpWyqXqS{8J_J>(2~< zCBaqymDA5~I)eBIh2A;!hwzLtES=^1?Zh~K1Z0yj{)4>7g|@==bKL%X>5~iQtH18s zpNhJZJ>c^RDPzDv?3YOXc=<60j8A%ojK`K=s~6y%3=~se_x_2fA_D3e>W#)bB3l*B@hE4P2^Qts2hn2hf|quqWmG(I-(SO?HNt}uO!E;$>WFHG_)Zos#wVLU1b{;hr~y+&vEzUEL7b0L2c$) z`ToO6Hi=)ViCrC8);f5}>XwxEw?eP}qK6JQcxDG;tP{`ZWxYG)xe>~znPf;zIaTIg z``ajAXBeY2^bA%A{&N!3- z7b2#5+8=wHXFzupRpigg-Gl?aYbXWx`P&@gc2f?4KiMI_3?Fx3txs=x`jdOK`{6bO z3yOe22>^=`*Zq~tBuneXek{wBtmB0=O=Yl!E$!Wl^0gUTImD#);n|++0cUfxRtn-@ z`At`d0CA7$G+O_aDnUe{H!?za6Ufcth#AMGem^5lGFIr5_bvx)WgH@=ACbNUYo)a4 zj@?j<${c79hgpb| zr5ZjSwp?QEgUXyKQwu+LMiMd{@lxBn1B-DPHxNyYl$RU0S@@`Pd^90Z)U8Got{(7o zxPm<6iKV31!j{=jO_&N=@0eLT7O}i%*F8EHNI1G+{mPLbQvQWEI)r4}6*8|sD(Ug@c+pNEmw(DbVFib*}s{32k6JqBbix)KnCDtlA`4s<@D_{`}@sqr<+oCb?qg$Lfj`I%{bZM#_q``u*OKoAclSDvBv6bsM=mG6GK%>)_Y^U zkJM^-vO*Y6~K5+@dwi7wF4`x5J_oA)T z@r{F!aE$HkoOQbZ9r)BfLR9GzX+m((pc&;v8ip_RI4k(>Mezm9@HK|=VW3_;S(WRs zHJf;9%gnHDG(p^kA5;Z--s>IhcZ(}<`p8{)Tk_a5Z{KR*%(0tx@_Eop@hv8k}u$bW`K z-2s=Z=&Twm;0MY=Lu_UWuL9!hhv8}{EwGR@WJZdvLCas*NOR?sce>`pLAake(}GF4 z*0GBl&Ra|x!qm^Ldzd=dX_%w%Wlj@O>jhI!t_N|c`%@hQZX4Nc&JK&j!dNNzb*HN~_=5UcW1j7S|R@52O4RYnjCU6yMWt-Cx{=MySyvZP|qLT|#oTUKX2 zT(d#H|8WKR{y}jaUp<5^3XAZPXnK)n?3)uZx}@?YzLHVuJ{}$YL=>iIGDlt1{quEy zfwB4^!EU~NnD*gP+Z-37_^sJQimo}^BP8G)gI*Oca8=J8R!48rpPNl@Xir?jC(N1u z+`0a><$gR4HrmRBqmTanvE~NreB|~ec%n0J5~ISQL2d6UJEB~r!TP=UX@v)R*<)s; zxof;2Rr_VM33t@!K!w}an1!-oK#f*2vv5HzSpo9MW57jjuC47lSE=NdK9r_XEgF z04lOT2Hj>v0M>`G#E6X~K40!2c~gdVT2m&vXZl)QS&1Y4yFD&(@T8Jv9Q0#Bj{zT7 z#7S!cPSRwdixzcnrMi`7sB^=Wd}xK$+AMr263lVD)l#KShjHfBz5ZI0NJQ6_;f(nV z@484k&n8EU2Ngi-YYv~{QhY$#NL9+XS6_rweR+Q;S#Igg%-7U*(~}7c9c=Tz-}glr zSZtJ>N;sR+|Du>r6Z*Ps*>vLrC^1!K7|_Qn;wI z72hwFZy9kFE`zs9>ugd~hCh^;`F^z{@IkW>UiA;k+p%k-YNe1{B)bt&%UgUryP(~v zMWi7f9E=r9v-$`F4l!#3y9@aO=L8qNeZeF^+pBmU3SiBmc%H{U`m>Cf$Y#nBds z;@Xyu=>qQocFJF%49P7mhC zve6lpLx(C~+-${uE!ZL)(;lwI zqe7%CJmGWSz92YJ>AvT*5~t^li7Q+e40CpZR__R;?{tTBugy(}IJ$U12Q(NTyA^>u zr*sbKqfA4UkVV~?0n%}+^+)&63uD{R{nctVVe285nadtDgIKZ3`)JE@DO zny4LGe#lspqgykCPt)cN9uZ%y1_pP)*WUvhrVQ`bov~LA{n*f6WNqI#Y+V0>%Va~+ zqA;$kAGZ;+PTw3)O|Hg&1Mg%W6;*a5DLC96aAO}0c|dhjatBvk8R&-v?ZAuMPb{*x@$rb3Xn@uG&81#~UbJB< zZ2rYd(VBP6;PB)(q#C!nm71ZUJ*d`r>SVUjo$3YsgB+|&9^aj{|CQn}lBWHgj9mox zx9pE1&PV0^sAdfm*UsC+WSo=nwr$%E#%cStM~NDL;~-ox z1D+Nfl6Kr_>OgoKyjD<$cjc6Gy<>O%)Ww9dZa=rR#a(##yJu$VICf2&YDte=>TS)QJxRE!SHxdFg> z?Sr}|7>H}K-!Y(nt@4a=?8PI-K%Cna_XE+na^}c<>ga$m$w1uzH$T?vG^R9aUE7M1 zNcGpN7cz8<1RR2!t!{!%Ok~^X)6R%>1FQAij`Ju5rWL}O{sOBm+SQ%d=;FB$Q@XO$ zP%6vm(Z-hz#2VX%;TvV z)x%GQ($0~YCrpEnw90o(A&e?DlmcTjR(xrPI)NZM*0~@S)q4ai>fKfw=avwL_D8Tt zOlB2t07p*`4dix;Rk4?~uZ?}Z%@0tye^u98b1fd!cssr=J^B%g)8$X9cVH=R7y1^o zyYg|8HLlrm_r#PBJZufodQKr_k+caGK^!ZUStGfv@0exKg|_5$fI#(TbW4@QU9t=N z{EAIm^F6)uA?F$Ay5*2Fs@K+R=||w2*LPqA9_ekX_yLdRqK8EH3GQ;}|6HJx% zB~fLS6j=BKt_kvjqkjYBS7lz;nXnlLCJ)b)U9IC@s^2@AM=eC80%E43l14tY;PLx! zT^V1L2Q*DGZCBqXq80Jxh_kfcM*yR@w zmcGJPxI^UsuAk;_Zl&0bopOziW0|cX^fj)UmD36mCSXJ zHS?P5@8Y-r#XYcK?4J9JYiq%HaAZDmbf6o4Z2$iF)Lqqp4+*mSj6ux9TS&ccb+377 z0oH{W6?@1aNKaCIR|}?uD{@nK)uZkn{2aumzpos)g;(ZIJxjjt9I!O#Hm8DPH7d($ z;xg70OHnY(e;mGHe(kJ9)2gq3P&ux;YV_;DC!xO^z`+{expqONM1DYnPgVf32-7s{ z2$?@{TRV>Rc*hNe#~|4h1@&4at1Fvv`T2(5S7Phe0feI|9fJKacs?FwKvY*Z^WlJwU+Hcsqgvi!g~5Om`@L8tcSOxrXma^sa7LFRpizcb#r;U zuKkrtD{Z8P~zm0Ze+Irtz1$y40yNQ;LgwAf5tdD?b*GN*S-UbACH2f1)dQ3i02RA zKmQF}fLX;w&3SX5>CQyex_@RSn!+;|LA5HsepZaZrkMxrA6wGwQA5A~-p=>U>y&kbfvOkyMPxFL$?Jaj92zX zk#qE{dP~r{kcSN1?&5uwYIzeExWHXVcU8G=G4l-t>#p0}Bh_wZRdZc~Ws_<$ zjGm8EPBnvw5wRyU#V=!qK~WTmM$Eb@WO{R6^!y;}xqMEi9X6~EPAmxp4(w`+z)6HYKLc9@ZN?yJ0V z{I_cq@ZEX=S{<$hth0avwm(+%y27-k07H87Y0N-~t)an@=<*4+SGYt2I1Kk>nkY)n zbX7-M_OdR0D)%t=HcfmEJ!4iiR2J8<3rmsKxxvKB6)fd^w8_%K&X%T^?|#6e_~s3N zt^P|7+araH-1=U7JU~pxL$W-LcVa>RIufu@W<*g>2^rynHyNsSa&ohG@F(R!1Hjq@ zlgIt_6NOz^a9UAhO#da1y%&kU*^C(vPl2ruG5;=WaC+^Hz=smhehPDwEGg30FI2gj zZd6_dwON^iFXDO(dL7*vFoD1Rano_<*#zfDzJzVyB(>#ZozP3H-128F>eN~C8&w0e z+BgHm+gm%ak-{bsRewKcGLSDkS_=%lD5XP;GvA)2GHOmpgdo4Qc525nCRWa1i+W9t zYVV43;FJs&O})53OnvWj`x$-ji28BQ(fi;Wy5-|>^02FVC_~aw2zWeJlFz{b9J-8y zExvtznT>QBUS-7sb`LYhKvzxLwdoKEYl73-;eGm zPt;F>6VmQfJ+@}B0#=(FY@ga1F)$G)1ylK(8`OPRx!ctk&z zNVyGJ=vStOa5T!ht{;Y7)RMV;f?c}O}&pQ*_eHxg~x88YF6a@@Xc$GY-I;H-d7 zfld^gVmJ#rR#))goS^m8Q1?T>&8PRz-}G^*d-AzS36)aBE4Z<|CZ4kgg!X6>>o2a< zPkOeYZS=xJcwg-(vyVF_WB&@A%Go3P4@KLr!fG|++64De*}a~=l^0*U)=E?gS5nHg zqwdtY#iihRUg4OnHDov+Cvcy&GdpyXm}cFm%Y9Zeu4gSozGwx4)yU=r2BFk-Ae^(r z;L)CDeqRCts&u0{TH`*SJ0#>O2A{>Ze|f!mv)YGeyU8807&Sgn@yVRA3LMhP&eRqo z#pmlEfPOXZ&F?Q}ZLH8o(={ji`uX@OH`AF|vta$8IlXy+kwl-ct9&@JZOxfDveA9D zV$bih7C z$0~u5a`Uxa@}{LS7nD<`b2RVe+J^kMUc zTnMju?dqL@5)q5%E_!L+Q*9=;ua?31X%v@C&GM$B^^0b24dv z6D;bMNq9P-5uGc2gV=Lp-;0dbX%1`Ok`d&dMX+IErx@@L6_c7VR&nv#1t9sX3!7}r zrli|;Tdivrkye$7cprJ~E-V5Ko=JV)7q< z5imJxUb~|!+xa*lD(Z+=UxVXf$GepNp_spWasejc#kq-dk@8+o8gGxR-(62?24@@c zW|0zV+kN2W2B%V(r!gvXh=Aat$&_9b`;insk8?n&97;-Q>#C2tJ7%b>Gx)Bn+rGgnne>S75_D zpHL?=Se%P;B-%S<58Kry&E#)>JVjUl@95R3crS5+ie>5oCv{5wxLVQU_fQY$X5q|c zC}+8=(Ml%CiV)G7Wxa^m3Hb4~Fx~2b9y(>z6e+ghLh91kynL0@tlU-6lfw1D^cyI; z)>XhlMo=PW9NTPrBQSDY%@&+&Tp#55;!h?FkRvaS^971!t;7Wv%SNOZ9DzA&BL^j( z5~dEy3Eg8w;VQler1W0k5~s+NK6vv24YA>Hbmp*RvuEKJukr35$k8HldL+9z!n1l= z?6G;N!y-#J_oQ#w^H%26Rtw{3!U7+7 z0!nW(gU2Q%sDq78;|&*=I;c7X(acV)jW&;qjLTf|iX7<5HT5(Qnn*gi+l}ad9YyH8 zsHV0<^SvgzxexTc7Nd_VTHJh|O4OMsTb`cUZy0TfNt+Rohur6CdQy|PyxV!{y8BKn zB75-MQb2lVY_T*+zqt2uaS%%1oHDvi?tJ3Dz#sjqb{eVL)Hp0f#?Pm$gc z;zdxcPl6?LJ?NaFN*MNW2syp?xIQ5exGzQz=h;FkA!a;AaHO>B9C3c5tOJ4QNh7$_ zP1~XSeF@iXivF_DDJxA!=;?73?0GD9Z5{@u0bpgmkYx}>Qgb}Cf4}LvUj*;RIR=EB z(GBE_OUHldy$=kNOFS3HyUBuozY%|K@#R%ARw$5*rwp?jEwNO5D21b2Z6vp?-z_G! zN^e4U_pzRors!y?#r`T(VWxFm>mp@TrmzNjTU#9VO|B=+SR7h3Kp`qTxMjqd-)R%a zf>M`U(iVDTmV3n%c;*Dep3}N!o0R8alD!WH8Qr~fd)8JmQ@q&M=;2^h#B!bb;5f)e za`B*nGq6UxCpXmBxZ-4Qy==}22hff_AZ-p`@V*i+LGysu&IG|TJi(NHl`ftS8kkcq z{?q*Z7}kp$qP@QQwpInFo;*PSmJQ8qbJ6wp!|8inFK00uT0=(K_BPkvXx_d@EXz!d zDQ0pf8=icjcS1KYF}675%WIYFl?-X5D`9oYvbrfJb0Z9t5UY+Jee%b%}cKsaW3!hEBOq&s}=_)J|K>|kMBbkkHzh@_2Sq0=fSlvL* zQ`;LKdTsPddfy*SZ;)Hyq68P)ZVrq(*^oa%mS=C>1!p_pJONk| z(Al%c<%(y^?ipmUC9pb;LQsNrA5XQ(i9)HYo~=&e^x{%}(!E_>(O;IQ@+|lsEEkU$ zOx5y~6ft%6`fd*H5h(Glw$kO-`tU-S40=+=2576*Zc-@-fxzvnn}d+bQ#R7HXM4sr zSH?ebm%9?f`Ma%{sy;I63=e?IAW7Fuzau^W{l{s*A0N2HIqFt}zCSaNjy|Ii({NB! zQJwHC&w^Dx_R|Ac=_rLp#-F}u@ulO29=%F0V!fiv(vc?J)7I|f_8EG+2F*7-mMIc_ zle%mT9b5rt1A>E6UDmkp4*(()@OCrr6FCFxr1{R73>cUbwg7>$ zG^sKZ&UZX{z8O3vOT!$-kpfye!yvJ9$O@ z6TN44qmoI~z3&+3gXiKU_B6~CI96MLUZTv(ty&%^7t2jt?BfCpFa)-LqvF80jZT4~ zBn_PP>omD+Q0lf>MLrMNhw4t!;I%VC*rP9%Tb%9HdHb%W;%&Wn9ko)I_z+I2vMGRm69-|uuq+Q~B`Gw|D!%iKqf|v?}gskMG zlm~NNiAVrGHAqV~+}+!MLf3|soV6XK`|0(V!{d_@=3OrR#hdXusO6L}7@ZAnxU&l! zr5nO9Mf{T0E+W5qSM*`qy>Q#^jed?6b56%>fqev6ANHyqIURJ&``V&8N;8$_Gl&a` z^<5MKm*?iE=jd$lRMr!+=f}?Oi0d*w`p>r@XR~zfZEuibLLCkEG$JUeBP7h=R^`~v)z`nP6lUge&r2HO`0aCqsa#c&_h_#5lIwf9Ddo&};Al9= zvl-Ta+qkNJ-zDy&=flwii9QjTH|u8kF3DMjm3n6Fv_&z&ah~me;Fa z>`$TW0*3<{$U}Pjq%AiC2N5og{l{+L29K!|Z3;AbNH&w(^;aBNw(a0Zg-A7WlWuce>OxgmIr4Jrln>UF~tA`Bm6npc4iSDl&Wl;DGkbV8!D-4#3kg|MWhA{ z^|a-xV%_C8@_~R1;?I}p2(YIb00{%4=89M zz~B94CK)x7F<-W!^_1BGlX6Q2SZIKT5~lwBCCj%WQHZP+405b4sz0$hc6L5-Clume zWX3pvKFbL(%^Au*qk0N|y>h_&Gs*$txkRJLKr_{Y7B1BTz2KzRC2!-$)$6suwEHdd zgjr~@U_^0TvH6(Z`%7*K&=?+=-dc$x{H0aP`Fr-1Q7TS=Z33;2gs-%6TfDQxJiKpl z#&aq#J7DH3Qd*|n@SaGs{Px|eC;&H;D4arnz`#iiZpkf$Rj5NOIC;t%TUN?;UNGOB zr_P}JbmsgWbwN%U*Yj%HJ9A`Qi4ib2lIk{#>=7~RJJLg&W7jMm5Y5_^dV8}>4Q@sX zSmAP>5(A=(03wb(19UpPFv@wr3|Tr2WDOO{jo)r0^}=+si-rN%TXgK|CNM;~9$>GB zZS9=FbMD&5$pao|$Ha(7`O`2F{kk&!0O{(7Lka}{HDTX}neldfh$`6+O7=1kya8}q z>E-D^!~qVqhgFo{S}sjEK-1yHQpGjo&TU|dOyC* zP;p2;#3bNCwI?wuE&W^St>|o|Id^~dfCpyUnVjh7{kQTofr9RpllnH`r9}&y#>W_v z4B@06l|}c3Z(t5s)nriV(a?5+3dwQeu+5%yvK;_Uezopw&19NffM_(0(H{{=+Qnl`VjU@NugFqv5`$9pE^OOhwrE$cwl)$jtO+2)GXdl^J9J#07#Kxl=J7hZmP zfxqInA(Nog#Ck&1a5L?rX$m?|*hAU^?nJXP^Q94sn^gTIn+8M(aNS5|vY890z4FvE zt$NcSQ_s^#1Hhg8jQ!AMfDd`d-(T+5TEstt7O@=%w zEnsge9+I|cX^S%zdw@ih+Dx{=y0DL%m3x|0AurZXoU=h|6h(Ot$a8p(Y$%G>+|86e z6V1VIoTH)M7$ub`E>yZiZ@TT*DsV-?`&-zOrT^GsPGlz`HahVO@hFChMPwO3Zn17g zHRy;r=9^)hfr&|ed#}CrTI*WZwbc8@#l(hAxHR1%d<=+=o{oL?~D@ zz91`8%4@wYS;AvIi?#j! zg4}6?7&Mq2xf=$~=gAhk{fpn6PgeZ-bOUR0M~(FK4A)KfyQ(J9$g9^j4QP(m-1wx; zh2GS7#kmd-Vo0fZqaYy{7i~ap@!SAJk%%VXcYg_z{e^#4*Z5D5#*m;XzB~D)PK@dg zc%@0fRbHKEqRVNu4YBMU$WSm#*#HS)*VkFCQvgM0Rs$fde5V~c^OwC~cR%{$v$-a= zs_nO|70iHRvW@E-@*kr8CLfj-?oSwYRXw&X>h#_w?Z8AEa@1w_`z;zlyHZ#;a8%4s zz*{F#{<^gZAWH9nDkE7+(xw3cs+WbHb^6R{(20bv4OtJu9F!YeaWz|XckZu4+$o^9 z5Z9jHWdnqd&tA-`+o#0W7Y()jj6%vrk*!un$cYvVq>csGL)e&q$se z*B_Lcfe1j{_whUHWIX8e5ZI>ri(j{6fW>hE5C7iTxgq#ZCX4q&X2s|mV3L>set;aC zV*Fje^}24+mlEfU=G*<2^aTpiV|>^9>qqE>U+>fOPl1tT<-fN7=ZmE9$yC&YT>5Xe zAQ=B(12p)oI)m!ZL*}gptbwC8MD90hLH#rO5d{Bl?g8(g-((kb(Nw6q(uMqET;QMO zKYpODv;EKlpSMNm{Rv%uR{+biQ^RW+$)<)rvbsm*gWWBG0(iL6X5!ibIU`vqJ+%aD zIG-&oz~fqPYG>`+uD;AQMxe)kqyVb_WdqfL#Wk~;Su*K?=8+9n=Rz8B4> zw{Hsw9Htc76XNihd5BiiVxd?Em_J%z^2fke?)No>dQ+2YQ-9j;y-rM5GhKCN)on@1GPzA5sn8}jUqXeS->Qv@SMl# zdg!5E(9M-8ztKd7zC)CBw--#pT--6afXN+)fYK+heW60Fl^uZiFQ zB95h@(q%2LF<){Vf6?nW4Z$DMWu;$#l>o&$&TN%5Vi)JtZzX1T)A-cd;yXvj2M1?z z1sCGlK6`fB%&qstW&pF+(INGMO?xxdl``G{A>`^s7_4^CRl zoYbG@eF!KAqxCB1EgdFujBRPYA@emoPVS|fF17y{z9r5NblCP=_tk#YtMMGK#+)wy zEQ|;t&D{pX$?(wit{?cLSIX7?>3z_?&>l_PQqbwOMayHt@vtIvO&s`!`MI!_4(`l1 zKrlfR-~=z7=JtV#AJARi9q1FVSXbSi<=f$o_EqN6&SM7mVeEODeud{Hz1v`rt{J8> zrN2R)zZ!Pn`tBf-L+QF;cIYtL*RVwKRaoMaacbrXF=Q<5&}77?Aj)}gz zssR9bxqYYf{K}Lvevsj<5Aspm07bdUx|DW!WD{sn<3*7O22g0h(aIebSF?nNxwOYX z+m>k?VnP5Dcy?bf2VEHcE(hgtXQhDZTfCxaf)>!XOYKPRR^qoqECP7}80*tswU z6)jVm*a0&YilW2269n2rl92Ns{aR}5eGG8fEzY99z|2|n-rbwn$?!>`WwiYKc4X6R zk$h89Ev_~Om zdx)y?B|y9)@wF0(D@)EhsYh6!qb#`_-ke{AU{lG1{4^~X_sBVRKYR-<9exH=|!qV3{0a!l~+~%araZ@-KTL1nidhFmDF=DPQ zn4>J)m%H`tA5vAyM@*^1YqhmJSTOsp5Ls_I;iM3IJE*~^(CgUmD+jlaf$b1lVZc=3 zlG3JmNMTKi8yJ$sTsSI~bn)Y!*x&S}pWPLI{&QD`$epDw^z|1#-HTAKMLOwU`zWie z?{C=4*uiB~<0NE-{FEPaS~4f=R-VUd`>~d&=JaI!!RPixjAyy?c@u)NY6HREh3=g< zc!G`uyVc9@gF!8T!nZUmTCutGm6e!?$l*@*N>6frJie7t%$^)Ew3kLz6rMOVRf0$o zU(rkKKHDu#b1MVM*kPS=)75UWh8L(>MXs@m-Eo1x@K3!>umq=#PGZ(i8u}Wn)7{CU{_|d<$ickFx{5J8Rsk(Z z^SU8G<7mgw-zDPEW3!zf(pT~(O$;t)g|fd+NX}H!dx2?OX=Mmu$ABsrkQqL#XcDUo zs}fetbATPa;`P+1A!#f#?Y*HK@&o`jQRWd8@ESTO|AA#JD2?^yd0{FIup@B{1;<<( z%thVtVxMU_3rWZXvotnD95P^GqoC{=YV~mB7+pll7C7_FE~R!SE-1<(KJfF_I{#;l z`mXj{Q7%SO5gh$lW`dRnLl(nso7jc?NbVDEE%9%^pf1~@{?gPEph&4m-+aO47U>t>w+ZmRdehy7znY*q+x)pA6Afk|b zrv$eb^I5Fz0xgnA6Ba$wMeKGk3nT#%aVE7!$)aHp0HTZ&$iNKwdIzTEL->i zV95T2sGCvc3@Ojeo3(08r^ylI; z6(TQ(J45j%#r$iN0%hf!!>|Uk33?$#jmO5er5Qm0^WGiys3_Y^tH&;iR}|uM&wM!7 zJ~Ag>Ah1cz1B+K2tNA{qvXN?}-vm2QsW)BOvQUSK$HAoR`g2mZFhdGyu8bvX^iXkC z<27iUUev+Z<1+;VwjsrPGcEGNC7Zq9oBFcY`{L7c4A1Dtln|m&MN{E69*Sa9w2LNg zFEGB9h}g>=QOg##V+~4^dDz?2xo5rXk3ww5*UDPZQwRZ^Cdg@ z_VK&WO-p1bx2E+I<+FC=2!B%DiM%_n?*mJ*2(_aYx==Oh1Fh0XgnzyEdNeh4a(_or zA#Qv5_A=<$h*2}k*I591p)hP?ijcL)gPt1YWL3=E>zfrwp$y1Nhds*VUof`}i=XFz zA5FS%joSw?@E+x_@)tG$p5F4QbN?W>^%-!kAOIvt5mvfaZAAF{zyom z;a*so0O{VT8Kwl4dZmBls$LURiPN5srtGlBEjxvT(VRPoRfxL|)0p%CzrI@p(;{Da zXLYTh%@+~1MwJWsRkR}q1Ca{?9xFB&QBZlKa4uX1sMtoG!9AeFYA(sIym*>V{-XX_ zFhwPHdjI?6h0pD~RW&hjD<{2)XOfx261=P()s=yN$ji9^|=T7`z(Fj`?}F80}uX)#coE46Q< z#nAkDIZHQaA&~?qYwaV`f-|X_jeQl?DE5P{%mO#(NreQK>ecbOPTKctb;DJX=Y{<- zK!K@~Z!T3Ph*EDT*8v(Zzesg4YrB#Xnk3oukC9Y>at5TDF~xfcZkPUV>4t*%XG*>m zehyXCsHL~qebrvmXJ(e&qxz99#=R-I<7t{=&1#YmKCv0bq1Wp66%`Uhygq31v~^`J ztW;Rzo!MS?NQOMBa{p!lD?*cf`*9~e*ec+U36~FJN(ght4CueWOiDJyswUko9v%Om z0I)&OH3E9a0&jotYNpDv(A6Wx=Kz&^u=sQ4=t;x84Z9#{he3H z-1GFM9u9DuA~Ahu*_I1SqPr;(vNj87X{~V}E>NT1eQ*-lMZCo zGTg+66I1}E|%PBBDSUs`6+;b z*AAKoU2+nHl&*I~iu$+Uuve@*fY2@uG&s5Gz28a^cQc#*QsT6_tI!cIRXA9rFI{TU zHS18s@xoejhN+P!JkH4sx#F|66af^Np=69=T*R|&QOTlZ3RqKxLX1_k<0#MBLKAy1 z@y*}rD&*!>5zlu+!WGUdTQs7ss~J-0L%z;phYq9*&4DuTw;;4zXjmRLOoU_oV8NS- zqcfIP_S{VsL+rl_!Hhu-jOx&c1nc`IIdE3giD1mR@m~V zpWaYTLMn}gPPNalwynl(9e?qY%rsnE8E&^Od#7H9LJ&r9BkUDU4geOEr&2TiJPG<+|Bo!7 zb{|>@H~8RgTy6GtUD9Ru**nf$g9>52@@J6{dpo>zYqGfSJ=dc$RX)R-`Mf1k@$4+2 zz9cj|!ywZg7a0V!dcQdp_klYUgjLapMdZTvBiF(}i|^Mk7u`lkLXG!5m0u_Y`2Zc9 zwS?Zk%I`mG1ph5$^dDgZC}!5-nNNZ#pl9Ixgq_J4xB@uuK2?UyXKi>@A_Nko#9W`Ht=a6ZQ9=XLgjE0H17Zlk`y+HS>)0MpsNx$8 zlJCGqG0B#H%iH}|ePB2H?&q!Lc>tGp;4mU)$Fo!3`|QLBc+J5xi9gSD124YQJNM}x zyT$fHr`2!7y2lu+srAcxzmY?fV*TDbX8&Nn+|u-<$)wb7jx5a*ng5CsTB|k#P|Tm7 z|1ucQwC;#vkL|Rt&+CajJPb}2a~6gkx)c4{k;oYOJ){{>Y9%SMehvs1(ZO+u-P^!N zsJbzQ{6(}x;z`X~^-n@;z|f~q&ReH_T~_a;kn06G{+TlVG+@Z~>x5ggvC z(yFoQ!K~^EJXneDVDmY=18_5qjeFVbzwx0_|INXZYRsx|=D_QQzMQ_JyrR)x+R8D^ z5E#Z72PlrZZN?LvK<+j7zz1f~1BSMk&c_E`LzIR(`rb=ttT-xhtv{?=)y@y4N)w6z zGclLu{vu`;STBaw_^j<;sJ1)oYp8ZEs;?_rYfR>dyIr8IByw;V5G>&Gsp@9^{qkq0 z)XF@sbUb}I6hwJFeCgH)@xh(luO>hfE1pNM^u9`(D8_-eoRGgXj7yV;zbJ+?GpTYj zO;W&l`|yi!)ByllBT09y!SJKn7}ReXi3slFMdhECT8MxVMylAWr?RUhJ+Lx0E*l#q zFdidbb+tTAxW2aU+6ruW#G-ib1`+}RMAlNXu7WQ6k(!okPgehIyL@1z|7ZW%aGrJu zk)2Ls?0j#E`eLs>8%D&Y5|NuSy$-vvlupPVOdX-Op*d98=s1_g=G2$!N-4gjad zd;mvU=09Z={`*G9+amzYMyR>oYP`=F9eiMY#Wa5nK;~Fj+@vR@z!&rv8&o*Sc0}K* zx5JmhmvZxZgm;!k4Jp}PXOkP8MKw)W56^U^rCxP3$dh^OQDlCF@YN9^S=U=#=LNuy zB10?1@m!CY^pOTNb+eR4aQs~`qIVe~J~k<&9lgP;>G#iW;z2mfBAzgKc-&U@b^n?r zs~H{^e_`El<~J+&$7#GWeDst*nQdb@5d6l}qK+%>;k(8HP*bXDtm3ZgWM(}l78?(z zVO*1Uj_38d8cgl)$#SlFov7tz$mW(4y=PO~hI7cs+|XClyBtaNSncZvcvNZTs$#6E z+d-c_Ley>2x$j_dGu0Wnv)>cwRhb~_+yEFp9)0$y7HUyul@2MMS>@Kt#3)Pkd4!~K z0LFU{+X+ZI>_vJkADj0_&_~?@XdT*nrbX}*8==tM?M4tn_fyMRnQcg zx*ZELOxAp@!U(X-?y@n(&9FH+xAuezD>08o>FS z>yfpvWZYGm$0heY_STS7bBm;i1&=M@e03KTXyyiQBaqeYO#fe`h!gU zB0ofvuVc-6s=(P7TJv!D5M=aI8rq;;{6J+{ ztb8plMpu6+lX z?ZO!*b@*IE>on-D3~hG^2z7XJa;oh@jkX>`$G-D2JN1Kl%*q457kh>E9|*cmOg9&K z1(ruFQ(3J2_3grg`NdSTJv{a?rEVLBr#71d!h&v4kC!$|p&4|sts)oq#^@-*>>ElD zPm1rQN^q`^ugy{1%U1CcYE~8t4ZMiB_C-p#I8c2Uax%{4&%|VwCcNlbk;l;~O+JbK zwD&z-)vQ;4Lu_n948j5?J80lo1l*UR`qv`}SzoT(j#cLST{2mi2~5n+HR8Bb@nOuU zMZY561|_m$u{H|BZtbGWUz6oKc2|P}_7jdhUJ)by9HWwlQZ}oa&J^|v(n(K36Tp#45^HT@EJIO+^j=e)yh*PMO!g{Jaqt~TTL;n=zWZe`LCK&ptS zdfc}6-gHar>Br>{6VwcKEl<@-qowxawe_NiVoLy=9}~GkYjQT)wpV|1K0OQ)^4fJn z64|9|k&4(U)G5&;-KZ?4y{K!;ql>M0nD*n97t*PALM`~aC7JuG`)1+Z#m(DbcT`2I z6U*xwOPpgla8FmYVVvyNwyM=J zCQ-=xPTM&5vopX358BBpxqZAE>qE;s-NhM-3;wh zvlA%;78%Uc(WKbm-&z1M0m1}oRUrti3PoB{6G$^ovw}bBlB$GZ%=<*v#FnSKeT|h| z`D2oyt*$(e%d4G|d`2yYQj}id00AN2^dOXBG-c1RbFGXQs2d7C){jdfsC2O1zLx<- z+WA*LEz&8eW38II6N+1s)X_Ij+O7Ql7VeO8JkW1MFx=i6jakm?%p0LvGOnMfK;Mm$ zh;D_Y;RoksMXzxuI~7iu!b;PdKid8v7rru_Og zA>T9KJ<@q?+g&#KVquTku`;8X(F}Kog(jWvPnr`;yy2Ob*it21 zCU>1qw;C%#1F_7bS9_63tT`w#P`GYCTlL(PG!AU;ht1a`>KJpLa;`ksTDsBthI(l1>mq&N1EY<7F_pyZ&no@S{3d}0^6OYp;tvJms9iqM zqpi!_bMKm0%;F4hIJDW`2a+TU&nbN`jyDS->`R@7j*4bon)WP6Ut%C9hunD%wtXp~ zhg7`BP<=iVhEvXlkerpas`$2so6R^T--4)Hs;aU6os{D*hslN2{RvfWCV8s3LkVBl z+8*Uo4z#IyPX-0X=vgFRS6;VWTJ>7r)1D7Z^Kq@LzJHo6T!-KWltT+%QvPpOOSmq0 zRogwP7ufK7_4L+#dmHc@8@(sYEQDjNf(1E1V}!<$p01Gb85c*2hU|tLm^=|ddie%( z*oCF_437Dg|51;q=@=(-*wXfhlvVxecr^mWbU$zu z6TsmY20){F4au1LQ6)TS+x_SF7RQ*jW^Oj)2k0c zt@mgG=_nQA+6|zeb-Q_Vob8Qp)jO{^c^v)7YwE&~7ZZSsS$Te-1Oz59@j-Kq`{BAq{c^!^~f&@7FyHi8mjUvx>*~a%&;^`egt=Ncszc2 z&Ff1B|8;r4{#@Qc*Hag`(d&|gnHT4uu#;NrUb4}iHBYnck-*@PW+KtzPBcx6l|Dz;J!U%`RV2Jx`2rJK=!XYeUTrxN z%;^kw!hqh@3KSmDE0E)`hM=zX-ewLPabE0f3zixPv@JUK?MDkaB!3S@-72YC1?NKm zFFX%DeHr0wH&JZ*uPYVvbEU>osjsf58+m(}Cl1EfA|2DklaG{!>N=y+ygEiccYc3= zl)RT6Nam|3Y=0~`TQyQ_AU4*F3*w;XAV=E0Pp>{J8#T!DA11%^kpuJVNvlQ&N`mMsvt*3EZmTCWpug_A=oV`2b@zq1 z?}1M#p(xM_)^$QNAgj`?^EbeJ+)k0HqZ>-G-YMo^9wZWs-vjnWfwwmqp_1@_UBzYa zsskBlr45bb4x(>Kr5uC_{klD>$;rtDJ58sr+7&1-eakFz?p_t_ic-zvBA5Ciga6VM zFsrT#)Zp;JtrJ1$EqT%l_C-Z#$BTIYkO@`Q72KM z2K!a2G3&0*3$sVJ7giWfop0(V(692bSUKJ;UJPmWEC z?kpA^*W~ZHGI1}!+GKgHmu_rZV!qNL#6;U@4^rK*KBR*?f8ziY;cCvBrplGJTqd6Lx>Kd zai4=Vx7^2bm)rIw(_dPTwF>v5e5V~o5Q|SkIlgT^+88O*_T2L6RruiIa zI(HlXphR3-L`vDnhE$cT5FrK+c9uVso#%o!{#dNVer-z|ZFL$pAx6C>Al<7$8M14t zNEk+9uZu3P#`qJGSt%WWSPbdroB{$+KOzu-zUzGY_+JCiOCSKW6~NO}2%vjyBd$$KG5aG^r*D6uR|E7i?Lj!Ry+yvJ4~pCVf#UBh5lkCzE5- zOXn^ya_XM|Uc&&7t@cckux&(J+ST;Amsb$RLzC(H;uee+I_Tr$12Nr(r!ip0wH`h{ zA!88B=rilXeBP3w{BAy%6pI>tDGWf1V5^; z+fvNa-v^gKgSUs-l1c6vlBYo{m9Wa%ls(uLM!722KXPK`!t&Xu4IK~~km|(C+=hcv zY}PT)B%QW)U&F_lPfrz6lk%yIw zVey={O;<0?5VWBue)1^SL*$v@fqv~KlEA8ysZmFlk{p}c9_*L`Kn6U z1>M+p)&jxT-)yf>-g^^wBP+eNrc&KHnTIz~NMIz$c8z{`w#F87u<-H{SGNAEd@G~t zI7HE0wDERwHTK7czAaas8CpBUSY`E82i=;?pzhBg zmvfKYm53XPv>sFA(njQzy&X<9PLKrrl{V|=`t-d#!5FU27!SIx7}0YKhc(`r@IB9? z2e_!Ufdst+`}rgZ5L*`EJyY>xWB}tb`H6AM6$c+6=7vBA*@pz6K_`|-#e=yldS1)7}P|& zxM##%z7cQKrlfMLil1cO(b3^4Uf{4+>B_YGjcCtn%rynMH-|X0ns%^$x~*BHz#-&t zy>5=G2|9NDUFu@v&iigJ`}ZRYJjN*#`%UI*84rvtm_E%;9x=s9dTrl1D5lh^awX6E zR%{&gG5BM{(E-lm%@uC0uu2c+W_C9%V$_Z2K=(K)4|yu@c>F!M%jdiGgmCL?W3CTQ z+EN|lXN18rzMudJ8}pUcr;-0=W!Eo(SCLJhLyj}YLA@I;btw_R$ZE6@h3fi<`mD;b zog;FoShvN7B4jPhN2W8HbAcW(@Ky1lA{&r3q|GSQrugH2&!{Rbb2QFp#65HBut z#SQ)VPS`w)1Q$KUwR{ax!`?ebw_z!qhv zHTKPwPEo$*i-a?qeT|XY7QJ6Fu8b##WlH-uPKi$bLAp{|sNephbAObqBQVmetv97~ zp|4PFH{K+}B{$M?h2-h+Jm-K098ny#f-7zo60r2}y(GFYpQkc;XKH(GjJ@x%NBI+5 z=tvTgE+SZst0qnYD&f`HSFpM+wZ9Z6~61}RCVqT@FqpZVeENuH3lgF^L#o? zr=b}rPVZTnY$ZBtEL(ZSRf`Hg|)5KRMFX|j<#&$Ztd5B?xFJqxOKw{FC7 zyA@EyDPOMg@NqjPG#^0qr`ppHzY4p`K__h2Z#mOjt1u^yFDuYHj+v5>u3GZ+vQuxyjb8K zj~E+9n)9t}^|MG4j0N*c^d)f696Wq}MYxw(>6u%x>|>%RRaN(123h;5q3>D?u162+ z?fR~TGMpu)x=b_;3W6D7W4<5FTRVpY7WT9=)LOCqqlxM?d|k%6WYkj*xR7E%{aO}h z_6fE=NRGEZT%;=+SSaxrb$ryu#EF?{P2&mT{ItC`r2B?S*^!eBiK95Fe~~73{|K6K zOqXx}dT%TmcGMOO8TcRtbz=SI&sRM0eS6;tq`D@WT7`P7CTSj8MRQ@2IWsts$q3nC zh+F6gx2uGgkK?C%M-#4?3!MzR%c*AsZ|2Okgh3*zH)m^^);52}E*2DburDzuPUqZX zU;MtH079G$PXU#8_uC9KNKlTfUAqJn%PI&hS*{y9Lx9B;kqJ3rAta-Ji;*dILS$i| zCpo+>&DSal;)WNrgL&!d_bo4i0kmaKV|9XB8hs|ML&+T++{nQjV~f1L9f?A|841p# zg2v}oc{FmulO4R-*&k_#*B@+mfHDq&|LIchyXBvsoQWpf)RU%|ta2;^QqG1&7P8eK zp_u>jUV1roC2urNp&o--a(6%hJ(psa0aKTkb)EWm_ngdd^^etbeeJ&$>-y}$0tA`d z`RnO?%r)m}at$X|!ai4i7Xyh^?EV{A_!{*mQFfCUP?ub&~sZ)R> z==UV_S<#nl3ywAe}{YCoQhV3 zob-<@g6k4I&1JuFR;WH@xn56Hv?_Lio6Q5esIAV(W_FoxQTq4p$KmCr2P#r*9fUjF z8$DiZdtXjP5cP;XWu1BOxJspp%fwHCnclCgSHht0#MY^)ZaCiU2yNDd^X|_c&f&Yl z9V-}U)|Xn}Upw{4XuUN>26_!2>!lGm1vmah!O4w|IY_4(~n zJg_-^Q43WS{a_RGvg(fBf8@&H;>2uO4siEwgFIJWd1^j(5A1wTO3bHxmcKvipV5E& z4}!=j^3HVweV8b#5av@!U6w=2ToMiGgTG9*I__>W2`cv4g(M8nLt2K1lbz_XY_Kc6hEyll0Vg5e8N@ARejr=t{@Uv)I8UU^*hi{zFz`3@+ z>rUZc2q?ixt|e9-$!dk&7m=n){q0G1I_r0NhBnSA+WHOVQ*ljJ{&(ot^Bx!f+9^=d zqExGLttKY)AVCF<9P3#9cCp))32bI)Q8CZfn>nHAEYLvr^Hc$2AYcViaTC|uR`!2c z+7gg<2jCL zrpt(cL!$0=pk9x|6EDGm)F74zRwQ}_@5K<3#+~N zb7{|m|Gj%l=r7x1yA9rr*Z|HW@q52>my89xJJbwdQ2((fpm4CXV)FZ~02n3({=0Jd zH^c7uJp4P1Bvxj=7=SrB7HlUk%3%lyju8>BX8XNGF#FkDo2EiMew`jFAnT~r9ZHXs zsI;*($P(iA?T9UXT-8gpD#7{pQ8|x4Dsqy=m;c4Z1gV0MwMs0*fgzMh{Vy!&?tOd~ z@nxv3`Cq`Hd1mnLEt==RK|733{Oc%DCW3?a5+U_3XX^noA;^Ro5CXuo|KoImI+P2p zub>Wg%3#XNOYa{zHQmEKw^G-M#kZoTod5DJzcL`;r}55&<)0QmlV9}xR)Fc& z9K%96ttP9$SmViamsL?kfIX;x^0E7uD|lW7Zv#Wz|Fi*zA-BQD@*Nr3o{Bx{>M|J6 zP~2Gqro$%-=97lrtMe(IpwO+c(X6`kKZoxZA9FX91Ao@8JpRX7Bh!X+Xp5^D#(y!g z5HRlKNf205Ryf{(0O~-&BsukL*(CIaw7eA4x{N3@tmPc4-EpOc!qDpouDjVL*Qd77 z7pJW!Z2hj)yGr?Fv&c5F3vG0~L<8Y=`ygSj!t`FX+IYao2_uf` zi@Jml3dzkLP?Znupx<6{=X#)`5*v)U-4_AX$@z5MVfM$B`2`EY<=|au{`)w74oyjwP(F=E!}jAWX96Yj(}<0^((msHfG+VlG1Be1Y;@Yz())wJ`xm9Uec7OmBS!9S~XI-@@HHFPzXZE92pe6+pr@1q8C4f!mF8gKckll^mo@)9mx1{WUK@3WqY@5*tDppnXgqohLa3rBVG@wH<8OmL+BIP|8OQZ+1j!Z|28b@mT3$jrh zsZRYr1Yq8f6tXkvc2(&Hi4s7Cq&h|06Ha`t3x2pRv$k*!cidK^?Bq4K%8(Ojm}2UB z(7lBAG>?nhv*7nbWsyBTqu#ZAU!s43ILPhYa`uLDY(_?bZ#Jpk`qqAQcfp0ajdb5# zuQ4*5Z#FvDFj3-BUaF1)6K342+`D|2uFriu;TwTKEyHB)xn!g@9QkT%A`CEVSi{(b zZ^ot)P)6gu;1&6QdO;`-MEVPaeorS?Z>g2H-}2<(=|gcTwEnj^n4?~gCzPPx;4C%KHM&zVQSf39B%+#cK}5Bn`QKg;hWbh6M6 zm@z-;JpVcf^jAf0Psnvn!VdN$`O`&P`;nBb!R@tWsZQ@~j=Tb) zzGJMtu3AOAxl!GG>T(d2U~L^>Kob*Od9Iw>Bbhklf6+g7ELvpPE3dlptzpCdA%$pz zlc+9sNVUIm)1hyb6YBM}112t5%b-@(3sZhY9sTa77rXNdFP69Q!0AeAYAY@T#vhfM zq6Q_nA73S_dq=M9I+47j`%3b4sEC!0HlvnS5Ybne#A$5|znPrRG@oI?>uxVe*}JcC z_rjYi8fz2=b$rHv1*P#a{USa!U1 z*V00ueM_O|JrWen1>q)a*Be*xj!m>@gOjZBEiT#>|GWZPG?{ohujf|JSTUy3*$pff zmfE#YiK4q#Mn})bnv+-3u_(nAlDK`K4;PaJV7<^M>zB#1wBsSX*~DOkQhB&1$}lfD zf$k+HxEErOO5cCah&;gB4I}PUc~t4&&31oAOmO*g?*WcFka$s3?)x0=cyG_252|B_ zzM^V$TInRkDiq^;yeD$yk#NBteqX5qFx}UaYq5~Xml$_o-#H6?)DOQll3O~a(70UU z=qq>@h=&g(i!-W|@Ro~H`lsc3F>6id@ew~{fZC=}nO&y*jMr+@GzWa}m7 zM|w?HLUvj~5n^MSSYW4K)p~UtONIhE4a;VO)fsq6yirBNz z^*aR5#6tnTUIv}`2HsD~GZ{RRuX)g89A?RScnROdK$B4G?} zhB>&(KkZ~ysOs&N%5=${#co@xu3VR!*?&+spjITxJ$elo{3MjsZxvtLricI28fnzw z0VGtQrR$C~-yLKYn2376Vb?R4=lD=uvd#H(^0fo!pzF}Nn&2zEJiN41JQS}kX7wfW zcf}Tc#q4elA-m>5LyYK{A-9OZ2_#qj;B>CWj1J%;FNTF+*LE7)Njn!ToSaIvDd7!t z(X9e9d2@rwj$>|ztc{3W1H+{HyRk?6`iZxF0x)IPnsNEoY5dumMJ&5t${jb2xEh53 zZIW)E-cq2_b+FL6#Ewu%Me1Jd8ECCf*imEn`QISY7oxr0RCVTx>q5P{8}^Pwp5HhHT_z^UeUx{es1XBkk*w z5dB3k>PF#19E~nDB)@XMCXt+eM`4VRPI2fG_i~N9{R|~KISgl6II4Va1dA4?xqYZ> z(OBRAK>;pA!D9ADPx03Q(-bSOb&B$FpY=ZXF+Wba^}u}*=rs$Jm@G0K(C0hB2(FF; z(ng9TzaV>l;|ixWP3mO~U(UX9RhFE-ezj-e!>be0^0+xJ`W=s=-k!6xn4D~*;XURJ ztseU*ldkA+P$q9bVDh=ldxg*tF}9UT9Ru}b`kj;c(B z8%oO-d~s#HcVu@;uBwzM;x6TEXj{~yb*J3Zq^I0ITMo^Cn&VbXm5^UiN~0l{jFv#pCJpiOmSV{ww<4=zQo4Ksj#z0o!6vjc^B+ zxiKVuCZ44VU3I*YWVSL{3x(u=>2v*(;z3ZvQr!e+0!!b-Mf1-S?JZ%>NFy2v4ZByqSzd8T@!Ff zA)rjbdvzgjcl|R5=T{L!60mvN2Rvd$7m8MYcS~`UrSU1X~(f_vdD%; zpwpJQDFW-80Tjc4X@A#TVQah$BU|;K7BCx4+6SmBM0DNEW9xmb1mn~jVV2hsVeMc_d|Ac+f$YalAo6mxQ zX+h~Se;)FP2vvENWIG=PFj%5>7i>$X%EF7P-k!n~IK>}~+GcIuF@~p_75e;CvuR#N z8bV&y3DhF$I;}T?cScwDXxF^QG1}ZdcG|rZ*Jqb6t<~&XV_YR~60sC(e|&OA0y2K; z#m%~xGMFN_)p7QQ}I=jpz`9*C>yLwa;g_0Km+De~;MiQdlHk#@peL1Wga zjZ*SuiO|zCSNQu*1%2w;!c`F(rQT8?!LGe5Jvutt|G1J zU8#I+=?-IDb{9*sa0$u zZtKpNZGT1+rT@*gkkyruW1xziL1;5mPtv4R_4#8s8Tr(l$Hr{6jz+x&iwx*>Y~MV; z7r-lmy?Jd?>g@89D`TZ2g_fgn9hc5H-C~GFG2gpH*p+?7i|BdDX_=Nq+jC1dKo`Q! zqPTsn+|A-nhV9h}j>5sc`g1Gi%+{p{E58b#+qPl#www6r=4Y?}sRkQhx-^W*J68~AHVW`_@KG$p|thFnv%OK)WY+rDL z*qJBMLGmH|{O2~&x93DoL3E6JOq;(y=68Qne@ALzS;{Gowl(aJxYDlZD@`DZ$FBUlck> zwfW$=wa(F)=bkf0IJba@-DU;}3w%E+Hf(yHG=7Bt0*cA4Tgsbgss_Z10k35y4xUx>L|YXz8E!&c$8~IURFmb;Z2Qjj>&3;I=vgN#dHL`I*Z3sY+DI z%c+!v=NnG>FZ^A)&`Yq36IXdUD?#+`Q~RbS$f6 zgV%HBx&(*Gu0Hi(oH$5F!*Bp2yYk|uEY45oi#LOt?+-9t1CU*3g84aF zlP|=i%Den3ndbJD8+U}Nq29HKPKNh&6VMF}aYoOS6XJ+N)h>jUzogXSS05E`@b%z@ zblT;6QkBY~45!_7>9fwSGfc{e4=xxIb~*dj(Ny9^TKdVPrG4TOd|^apWEXl5U^ULO z=_7WL1`<6OR~B)IV>n@WiQ>*R;)(@lej9H9qeSXHhu7NYMk}%{)DdVaEUFnBGIn-* z7#^ zGe^8u!zFa^-LTJRqSa0c)mz5i?|8?p@69@O#?wTCV9z+WrpF?-n8?T69VS)panfr} zjH}XDRYL_#ew`c}%l!K^OWo-%WZ^}9+~FIkHAx(99~$m`Hi5v$>3)+%4z3HZnWD^vA2*VJ|9na!3z-12MjH z1Yz`GLNK*Zg!3hBKQGa7jq_Joi^)<*56j;*3x?LUdK`1`ji}1j1**4_I5?a7u46nw zLx!y7LOz89rv7Uh|1Bej`3 znhkW3u@=W_BgXaJgwQs8D;DQ=+SUgICzUqkQdQ`!Tznri6IF2<`@_CBsgE?43}f7M zsI(wLW6#JK_)4}>dl7Ta5t(WAMK#~r82ZRS%@Yga5y{gw|s4_G^%j{4|S&E z^9346DawUfkX*u@8sg1^tmsC*pJ8Bppgkp3my0tXmrKZHolil(!N~D1T~1% z%)4_mboK!LQJ+nmWwY{#e#+j*QdiIF`V#G6N(4)9GS`caZuH)DvjStZ&>?4QBYHho zRN7;b_uOrn+=OKaXtsj3#fDjB2!u9BJeFpBw+HQyN^EpHJ)wj)i_v~>o3%5OZ&f=^ z!aaQ^J*7;^cS@PD!$d~zM_{TE%;J2E-R%EZRht19Q7D=dx+QLUEPa8Tec`bFm4DwHeV`IhgI#D!v?G{Y0KrHVnW%0FJ0 ztLnTo{NSc4)Eu#kwn#DRaZloFDaInN{2?P-F_|z3kG$WVDOmQ-j1FrjB>W+DZ7=7* zjcK(<9Ok@DBnYajvmJqO@<%-if&DsUPx9o=38^92H-_n>E1rIvk zxJEMN>}3eMlcl1{a8wyZFY(}Ou5G?cZ0yAd?*i?q%I)6cxN*XRqM9;h@qM%ZYRC~l8IFI&H0u&xKnZtkLrPm ztr<3|+F~A#ei3PXXMfJO+KoOnVq6nsQSXjQ;v~V4Q(&wi%d@AmZ9zW^^C2agJa40-sWsvax7icUaT9uhgf z6UsD*(^L7n^nxJ``4;4CdqsuI19i9Sp37TJ@Jk(-c)+6d|u-*s`z1L zM|ZaSr0+5M&vEvU^SBL{p7ruEy9nCXZzeys`@%NUZ{|HHc!oJOvc}my%fGRgK{$14 zx5uqFIyJ#57!&81LYGp)ds16(dGCPySx7B=+s2cs+~yPUU)wQfmdK%v3?gqQk{xV_ zVWufgH_z~ot&qLdVooOY3cE6ovw)8WHj!twIHs_Yj(RI>aJhVWqa*R})2D{)Zn1JU zJhyLTK8;pJwTHI8oP`p$D`P?j%jw!npmX z-Fev2X+-KgGkAzTRU#ti+dv^ zuB4-?5Ly!BGSBpv1@pNq=BH239DN1V=bMU7$TOUXVG;>3MJ(dH!*;M0u#~QNkS7

Ad6$Z3Akw%j96Y@|^JDZ!jG`&0tiQz%%63ZbY9CMpATO1za9+0`% z55OU{`CdV}vw6Mw{I!rII)}3&S*@jl!=Q`u;?yOIf_J$Fv#^KLD zP&e`3oEdp9`pCA5lqDXS_|OQr+Mv#XvO&m?QG2v)bRwk}_`w9e#ReTyv;&4Uy=pQ~ zgS6vq-OQ$FOFU`vIKNS*wf=oz6A_qvqXdX3w^SXq2DD3YYyF&Nd4c4D2~qv^q^OcL zJ?ENCKL{DlBIyaILZ61C?zLBAPsP%8<(CEmMOv(<>_R%KY)HuGjw99K$t6JV86g%{ z83r}fPS-ol>X~L|JfIEAcpM)t;2cQtg=K@~aw(&blYqSKxhD6^lN8q0MCm{Slg^XD z=aFX1!_z3nM_zhVRk;( z06~>DK+Mr@(O*3|+^$D5^J6;fe7?GEx4vvPng)JzIwin;KR|0V2H6_lu zI9i&!-^5a&#ktp>Qa+<7&uFSxqwQiXO>%B5}Ud*`qY4)(=Xm`O=>&oY!FQM1b#69If zQgA^*9@8nWU?h~I@!QZmZ68ZH=TuQxu2E-kFjgMJv^bx<(0RS;bdfnUPO_N2ykOTD zo}B6vHI3Rjm0?w})Of@(d-klO?!#9fSWAcM92^;f+_S!Ak@#yiJmXV4jy!h1qe?!y zQFxx7>}6lkl#6j-7VeU~l$?A0k(xI@wJexl)6{f*JT1Wyfsh_v{&hse1tXl2z{yy1h=zn#ogGD$4a_t=nrl77(QzY5q9N|=- zaL+~~6uW>5Sqz++YbbiRb*!g%ndb3(%^9<`YSa4T_uFJ1A4!N>XS?Ed0fuq98 z2=kE*i*!Ck(eKLqI;(S{{O#IeHB6g9ifHMs8|^m!9}>M>BIjT>{!CGR z{HIv#ECBcNGa!QInSOv=O=Ox=Tux?#95<;^%3Q^QR~iFT$AeBEO0~v~2^x%iMjdJ3 z=`M)#!U7wZvJC8Hd4|j(Ja{xIPwXd2I1LU}qA3d`&k(!MpVZl_mM=9s6mNd4)W z7gQzCEk8MfoYeI6Wx*SfEgw0?V#jcupXi&VR1-Sc+r;u;=GE&nz*B7#&D&!)?X0dQ z@z=HnU~^tgY*=KZCy@?bv6$S;toLlc&-AZVetT;$J_UL`soe-+5oNfSg!-hOW$okL z#^4}`B5m$pw7O$%VM##xN%MtYK#SP?RczEu*CI8zR+ zd?mcJDP~2hC6Q{IqQZ4jzfGH@eA$A=65|d;O*nD2GAY2VpGRtWiq;IOPH(g)ZfsPk z)Ep`Ic4dI1*f;yel1aXyt^DqF>rmg40^NfLv|X`3yhdK|RvIDUekX3wg&b|`YbOib zoT^(?EzkbE&MU-QS_@Af?@3$sdLCBJoJen>uFrPJdAbRs=-D7paEHp4Z?#={lyGKz zk#Z46aJbO7ZnIu&qW4-|Y^=1X2aT900dt0!xGj00D0FB=^tNsOIV?*d1Ek!I`^185 zwA1TzUjov2CVf0eyBhALK~G4o_HeMR7`!1TY`@r}B&DM-nGQWwdLrt5JISMgA`s4` z-QZ@Wm@F(*(dIdv2mV;Z-t3_|9@%9Yw5{LCGElvMai`JSlX55cB;X}A(3`&iU%OL~ zj$4=uU;IG`!QOme3jS6bIshkWTS9F0_GES;J8fl*?2Cn*wyl24pmf(nF2lajPHAyq z;QXm%&Q&gyp@_xGyt z_7=-c|H$%R)1JTJ!T&rG`Fo20ed^%f>DK>K)2;ui^58*bX%=GO2(rg!64kn(>!wK- z>d~S*_pyIcZOk;$k{7nl{LZA(_v`4WCl%c`5c}1Uf^6YCjO-vY%5m|FyRJKU3}~t? z-zBLf*cVEEI=^(^HdcS)z^wB>J0-4vBZs@lnIL_6Sd-HFA@4tpV0+2+0$1PjN^LG> zGBj%(%|s_Yu`>$q=52?dqs~?3)T2H=s@xs*jv1%8c&U$eXeN72N1(u^sSUN!Y@B=F zzP9PFS@2)s#GThMEktV@sD@!A#3<36%Lh1Bk*)k-c!P34^Opvk(-op7SHZQ+Umn@Y3D#ks)RP%JB(8++C-Sk&izL!0a}?Q+<;Nl*0r>SypJbam)g`* zuBID&o}NF)l?>%%$Uxs`uOII6-eWZ868XhjeY6D10xgiH@)i3_<#KgG^Z7@~Y5&3Q zh$x=Ja6DKSb*aQmDwu?`h7XbKg?y^>k+F}zH4prH^5ER7=hA%ujMj0s)j_5q52xx% zv)*r(YpL0p(uaz$Y+H4htINJ6{UUFFO8YBi6d%QNUB~$>H#tz(|+Zu-P_j&NYtq#!a z7Lk7HQ))cs>4ZU7;TH#p?pf13r&XCg(Vjb=%ex11+!S6&KO#n6j5+xgcl4w~S+JdU za?j#bRbjdJ%a>p*RnJ8HbW_4-5Eb))fDU$wF4W^E!$A!zE8{Ch=>ezbHSu=4w} zGott1A6S{H)E%QVobPx*og}3RKbU>ftqGIlv9q zrIpa1;+iPbJ*R1-adXhR->1JxBEoNB`4+g zYRy-k(jDY*FL#xs)M{Z)&2MHAt!Zamf1Szx8*M@?c@Nm1Ov4OB@A1@_6HIMkuDqs%7|L(1x^inyeP2kGj(V%n6U-7?ZnKO940o#yK z{a#)FF7_8^l^3p$#iYlZGhFwo`AOp)ui{T^pfKVGjC)t}{ruCfFNgzyLf4)1vcbDO zs!4zZrw_Yif8n>C)bD~k_sSFa+PV20p1o)!z10+@=o_g2r*A160Oy0a`EPuq*mJW| zR_Q*(^ml)e-IH^_v$ktjZf}6|ZHRN9FDvX7$ZqOD0bNyE9`GwGe}4`54DjQ0!+!7B zZtd!LSq`YiH$qQZ?fK$?#KzwSKK1m+aO%$mRkn^bm6V*>d;CFEwm$|YBGHfiqSzF7 zpGFrA$S6SPXzk%WA^j~DZU1-h$W}q2Ok8_p_fy6nw>AeK2^yOJNhXJxfUik1NB-PE zT$BJ5AQvPUbbZgD|E99-|1Qe|yh()@obxja!#Pv zOS+$#Fc=?L;r!K*r~fhON|POqR-Sl&veTnx_PI1)H=^BLy!x?e8XzxW`f%dC2TM}} z92g!B$onf-|IPHIjq&b4NuvCrr-Zxg@Kxf*ro#imO6VDYlb35 z4-njr69B?$OK5ow&eRB(ylR@-L>PCm^@4u=`ma~%fhluclUoexhGZ#G9z1!0Hz?iGW!l)oAGj@VO&4~P}F>d8!FlRi9yo@KupysfHq?b~;?N<^sN2`C-jhtu8}2lW9zVzS>M@(+ zL~euGdCYhwQroCO#I?ur#IeoVT#~QPdskv+S1j@H3CWnHpC3BB_ri^jP1&bBo?1O?1>7 zuBrEvA(8sRAo_(=f!Nq(46VM{Y({f<;cK7SGr|;p(qsap0-BziZHMLs9clA|O&j>R zUs>2$I7LQxz7c_`ZU)!Cq@jcjNl@5UU72Xb_Xgvfh080erWlWo`cG?^Piz1;@@(Y5oJ-E%Qx3PCuR_QG%c?f;ZsP3Wh&HP@>nY~D#1+( z+Ia2=@yYAwy@(r)5iI*i8c}xZ^C~gwfbdmzu12x?hPSa5#LH9}s|VmEOs(HBb^=$` zVxels8;m{`^deHz7Pe(~$SstZ5U%+>iSGcyD4s-`XdO>tlb;AEbD1otxHU)2B?cGZ zqH9zZcb93K##pJuLL-MpOE;KV;e{0;I6V=d&sA-Ac*pTj%1ya6@*P{$yDM!j zL7iY)`*-U+jCbs&?qaV)B_F1;Hmqo)*r~wm+a29*rNT3SGH#f6UvI@nD)inYxz)%~ojBmBRoe)B7{BZe}xGkoKGQkTON~|R3 zjS<3js`Q1RuhH(-}Qrp&)bPfJk31mjY_-Pj8#L3Fn zW`${z4O#jUP0EghajrSjOKLov7c^{xY7yKR(A2GDRBS9y<8gkt&Y>#V9qNipsK`J} zAe|n?eY$S^f_Fpyp+TG2F{EG58Vo9`JJ5ODO<$+L8rF*daf8eYXe={TYje;>9Biky z)Z$hTIEbybLcoi^qQx%By}HNaI$DQ%K7Pnh)x02HhmgEu?wm4%0_Mgz-Ldoj*iLzD zusQ}8X~Yjltt|Wy**4|I4m@{kJv_N-SL?qBl>UUcdWdrK=+R0LoWg%EdNSNu}_4%0_brn(lYM`O?#H2Rh z>4`P4xnCZ;8wx{!XvGQq&g$I~h}Df#AYlyV**3wiGN1Q~0?iWfeUFho4=i@4o&U)_ z;3=>1k#)~UXd~WF0UHqzV!uQjv%KVv9Tup|F|BWC-To4@ z(cuzE+fcLp{`heRSS=*Qu<>&*BKIs2{QaDj;P%4R8q(%a$g*|RK267*a)WwcGp^g6 zc*v3eMkV86EUs4+f$$3{<)SJ~9wG-#CA^#4k?*lBfSI~XPI;0o)R8636OPEx6Ql% zyA;2C6zPXi3Q&t~q)WbH=`dgaj=Ul;f<~ma5IgUmw6+;%QLQRf1idP-uxx}2~@+g{S^`7}U_?qNSXkq}~ zob2tVrQK&8#aB;N^W+q6%<_XDO$F27MzBo&NSEHM5vGfkPf&)3OK_5JC#>Ci9oR_B zwnI;G`zbO|P?7^3*Z#HK$f`MWTrk)m-yVnc{?~3j8^vFjx{9P2gCz3UL+K^CTqYuK zSS)Zl#3pd|hYM;BBrz3P4T8j0FEHnzg_E@!s6kU4{Sw;?pOyg}hwq-T%=vxk@`^N` zJpoeKX{o5ZtKYuu>1sb$&4B9WY_%!LQ=x@Cw4F8i6COX==#C5&hGZQ2yw_nWa=|*Z z*FouW;I=im@i8;!Fdn%fbbY_&AG!2O&3wH(p&?*W0E%d@&mE+$dCt_E_hAN$j|`w) zE#%2T+w!aJB)33vAn?hvpWMyxzgSv~{uYhdI_nXZXQs=ony73m-X>&?r?;f~eX3Z7 z)dqzz@fA2dSQs`rGE?15H-IqX;?%+FET#5bk^H2{$n-u7R~3TX6c93*NOsLYVUr^> zdF#bZ6)7VuDx@~fJuqvRv{Z~ZyfFu3qGjWbBKI3scnP6f?eBvLRZ4YlJQmMbTYf#) zX(9_a(v3fKBx&suoUo+LgW7kZAA;|Ez==uP+ZuOErl?sCR6#0a(JoGrj{RMC8<;pQ zZj4pR4$M{Xr#CMwh81#9H%P^GZB}+SRI(K=y|r{j>PXzkac*X-=RSg)XGqRl%W7Bg z?0dReH%BAuz`ZzqZbpkIfX;8Kpr47!x5WCcUXv}^bbc0S@UgVC`+XS#$|&2x?lJ)^{*WZV72R?F6c>xp6m zY$JZtUe^A*J`E#XkzM77Ar}V{8;yv33&+;f^nr*TkuTyIHUkSwUZkz?RVQ-KqQS;5 zLo=>R^%OI%8#pOC9?nJAp~SKQQdj%ixtq4aQsmJaos*bxo4dzJ0WFkK|6xL2CO|?y z>Cq@~Ml(5&y?HoPJG3&hg4Lm^@zGgK(#K`-O#LA50{|mH)0Yo z8eDEQTWOpSBj`aEVhsCpSYcrpp{zw{Lj9RhRSmFH2F4sV^v#UDUJlhQhpc3B`Xp47 zC*F9p2yj5EtAChi2I)Y;mhtC{U8fXegFLH8(=+fFH6>RH)N@@l2fm0+!1Rlo)6jkx zrc+OCAZlvCHU807-?&&Y@!{>bj&2=x0}I#L=w}VFli}i$rW~O}O2`u@NJ1Kl6+hVO zF-y#z=Sh1TO5}>InsR!buCba5Xxr=$vu5L&5(sp1O?9}+X9H`Y|1L66Ypy}A2BVFX zQhGFJRy6Z@D4B|bE%sqauDTa#J+S;iQvnHR`)l@XTd@v7+VxFA&>7sBx(S@boS9aot7)hT!uo1%B6Ei}LeGx4Oqe%nF;LjqSW~;>AldPVd1bX8%=;sTqGz3LE z&v2y*r^D%z&3v2zmEI@&Lw^7gm6_C3brLsCWSW~KJ+4HfCM}=#l)k@#KtHXo4>|WxN-~Rik+=Y z_T|}}X{dC6__jt*XK7(zCOlaR(TowvCkG0(^M7b^uawN!kC_2Ocert{*X{$!=;eEB z9cqk$0%8b@)e$p#CJaawtrGgKH75LVF; zoy9D~n7NgI)B< z6=4${nVJLH@a3zCJuNgob>XLIHfQ-PDr+#o(eXNt#BkvHknou=n;YL>&;6)RlF~jC zrenLQST$w_FIw|gZx@SqdarJ_R+4U^He)UsCwD$IC@j8X zO2-|${s{mbw<@D*07QYho($Mmr%T7Q;w|GNRWAhi8Tn$=kYOV(EMF+ebetxfSz+|f zBP~m7tBOqam5>!?uWwpfnW{hdKW8tVtQ>qfuy7g#lkCLe%XFw}~5vHuGF9l-B0IVPscx()57xU<8o+pKP9_C4nf###OeSWkplr z4t_cDav%@uQ^V{~S2R+tJ0L8H`7w_ZpP`c=WDOD%)JgJ-K^+$ z?3w9gjWyHdtuGjCTVWR~*jS(o^z9LMwXnZL(8iwQk~)!gJSYHJXsI37TVR1oGhsI6 zj4E=j3k$2P1XJ7OZ51;de_YHQhtq`{$U~L6^9sg06lRuu>jIc2EL>;SzF5q=e@T5- zmUT7*tuA@QF^G++nDhcAi=)F$8;6C30ZyR`M1b?LX%y+28W4l{fep|f?MLm8AwymT zyx!jZ08q_;*Y4-PzU}>AyOH~^vr=4N0su2quE+eMKU^7ePv97ReG?@U!w9m4KxC~U zRM0VwjbI*}&Zrj3bZ&St)LC<(S|()vuDKRb-p!p|vLk>spkhfJ_9LUO-U4U?>{(`7 zGGJ&Kf9MwfAHwC;FxS%L?gB6_$~Y6+Bm!i21xH^6USBA24+cR9UktA$*v1iJtN^qC zsw&62AK82o0zCKrkK9bw98)+LAa@M}45Ii4W@spfJ(mmY?58+Bge9uexB>2m82FOWG<8om;r&?fx-tvM#MXCcWbMbb+Uhw4?lo=qg&+63xw~~@7M$b z2LM1cmeNx12Mw-Lw+rSdPN~{SoF4Eo)FQ14J(kNL*6OEq&wO`6IP5^8vV*c z5hZH)Wx9a-b+1G6m&jY&W+bX4ZQgA!D0p`NM*aa&TzK1;BuWj~`n>A#WEhvE#Bkwk z=>y1hF)HH3wZ5?aW&Iu=87w6z>;pt1r~YDwqGyzaR$oBe)9uB0+klOn-Z}}d%l@_3 zY1+@{aEG1X38bp=f*o>fdlT)~k!;tz;ab3n($=k8*9pp$GlP4zbpwJ~009su?0i`> z*>@cisnC|IB@k-;AGdcz9}bAVJ~83$e$i)K(^abRB5la$#v8XB#x;QMxprD`ipB>} z6H-zIhHP3mn|y+tQAZkTVDjYcz!aeD-r*+v9r^Mi0MNslMt1)mlyDqcJdzHt!D!Kx z;CdUS{iPl&){0D?>B$#G)enSq}We zzYN?pIBEc98lM$dUVmzUD{ecPYO>y(I+Pnfv-VcXcOxLdHw8hhj*NN=HF$_L1PODK zN6|JIhEKO>mOxZeb_KSB$RaQ9vny#?A^`yYt`rPb8SjUkNb)n(f~}XF&0*v)&_PDI zLRBW`>Bj@FjkK4vM5$^W|I|D_1_y^iY{@~eW9mo`Y^RGvT>Vr>8?K)Sjy@1nz%NX2BaP<$p4?e}lA{6(f3MX40byG63$uXli|aUeo>gvk5sH;B%M zF_I|6xSq)ah{z^Fh#-&J0Pwf=@49(ia|uuagbnehX3Jjv2#Hfq!lsO5{krGv3*YyO z_H>SlY*SdR4b+IvL?;iZ5hw6fbhS3P93CA1;unlyR3l}gB}qoXr$k#Wrm%X$y;_G7 z$E-_lNo&>TG1kLvBfFOvL?({It-gj9?gToLt-gZ#)ssH^0$q#(K5ZGcCBE-aURO7= z5z*HvbVke2T5rmI_;vct(lLRR&ZgPLK%I_s00idwaCk=wPJl-*w<$L4mx3kPXM*-G zFtL6NgQJMQG!qu@2;igSdr0M zxr-Q4OaGWRNp~{*w5BYTFtrd(zeqJ~y)9 zGOuD@!1RQBH>HyJ18v#jD=XCd3~x&bzqhk4Z>3g{xYqvE3gDB)hbe)+kF>nbjU9!J zzXu{kPk!W1R{n*W?Z%EGK&+gt1%kj`+;+R1_s{wtSoJ=#ZvfbTWQ7WGywXH z6DwZYuS0+!Wx!zaD0V1w`@Yq~Xfk@YPgh%G zW^}%Ae%K9HpS62m#0I{cv={*6zGm|A@+v#~%T!rSQAkB5!2G^WoB;)48(hv|mp z3nrQaqeC?soi+u0B@nGr&h3w!_P{HcqrToSAD%k1tdMFy7s_{0xB5W+Fq>DisD3o; zY-!JVq?M>KV|<=@t6mnovrTuWOpIaY{0P)lecoisFVhH_EekuKaM|IJgV(67_8qT< zQ=-GO zw-*!=t;46!apy2;Z8H?oI7Woq4sG>KsmG2*Ff?R^K~S969h{oq=sd#W>@WXl7iwpcp}q$~FmtU@x6PONKO292i3A z?cBmEPW4gvoiEKcL(wYn=-BkqwO5%@f~HvCHE)Yo8b%yKD1NjZ_c zx_S)k%s1x{vn0h09FIPT>uMcc5T6BmU4~+f2dvSdB3-GnjZ3o=tm}@=y6B;}6XO@i zZ}BHPVwg0z=@=_w;n~I=wC{yX0!vCt7DnTuwm-9v*kbMD*0#1)Em1Py?}OjSweE70 z7LMf_b^^CpIg|Yo{&m^*RK!}flC>Bj_hJ!;3uDO&Z)X*-If!EBMj^bX?9-I@V9`Vr zl~r;5B5f2G6i<)#ctec9bziAir%?JphlV?(k#xt0z44wd;O4{AKN_yo3XMa#_m_oR z92BF8jk{8~g%UMU57HCqR=IObTT*r#$^IO#HuTXfs*b*fx=bqEERrs3p0gtDu~Fm9 z`}p3DT=zI$Md@_+^r&$Y6+&u;+w-65p`=Ju%(X_jVl56<#8z(DUJQt~#}4Fs?*q)z zF)BbyH-2q*M&dxdZZB7o>3Fc?{Ph(MARDQDPyUyve Date: Fri, 21 Jul 2023 15:42:24 +0200 Subject: [PATCH 30/76] fix: ensure resource owner in update human profile (#6253) (cherry picked from commit 13e284dd5687c87327b8b8b04d31f985a3cf99ee) --- internal/api/grpc/management/user.go | 2 +- internal/api/grpc/management/user_converter.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 5375ce5fb..d7208c5a4 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -421,7 +421,7 @@ func (s *Server) GetHumanProfile(ctx context.Context, req *mgmt_pb.GetHumanProfi } func (s *Server) UpdateHumanProfile(ctx context.Context, req *mgmt_pb.UpdateHumanProfileRequest) (*mgmt_pb.UpdateHumanProfileResponse, error) { - profile, err := s.command.ChangeHumanProfile(ctx, UpdateHumanProfileRequestToDomain(req)) + profile, err := s.command.ChangeHumanProfile(ctx, UpdateHumanProfileRequestToDomain(req, authz.GetCtxData(ctx).OrgID)) if err != nil { return nil, err } diff --git a/internal/api/grpc/management/user_converter.go b/internal/api/grpc/management/user_converter.go index a3f7760b0..fdcf3ebdb 100644 --- a/internal/api/grpc/management/user_converter.go +++ b/internal/api/grpc/management/user_converter.go @@ -146,11 +146,11 @@ func AddMachineUserRequestToCommand(req *mgmt_pb.AddMachineUserRequest, resource } } -func UpdateHumanProfileRequestToDomain(req *mgmt_pb.UpdateHumanProfileRequest) *domain.Profile { +func UpdateHumanProfileRequestToDomain(req *mgmt_pb.UpdateHumanProfileRequest, orgID string) *domain.Profile { preferredLanguage, err := language.Parse(req.PreferredLanguage) logging.Log("MANAG-GPcYv").OnError(err).Debug("language malformed") return &domain.Profile{ - ObjectRoot: models.ObjectRoot{AggregateID: req.UserId}, + ObjectRoot: models.ObjectRoot{AggregateID: req.UserId, ResourceOwner: orgID}, FirstName: req.FirstName, LastName: req.LastName, NickName: req.NickName, From 7a03eb1d7c7c3b212cff565e09c1d29b00b464ad Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Fri, 21 Jul 2023 16:57:09 +0200 Subject: [PATCH 31/76] fix: emit project.grant.member.changed event (#6252) (cherry picked from commit f6bc0479f41774145eab8cd7e1e6c23559406793) --- internal/repository/project/grant_member.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/repository/project/grant_member.go b/internal/repository/project/grant_member.go index 709067981..2c0f41041 100644 --- a/internal/repository/project/grant_member.go +++ b/internal/repository/project/grant_member.go @@ -109,7 +109,7 @@ func NewProjectGrantMemberChangedEvent( BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, - GrantMemberAddedType, + GrantMemberChangedType, ), UserID: userID, GrantID: grantID, From 3c1f092c7442962517d880da38ec05075d769d21 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 24 Jul 2023 15:48:24 +0200 Subject: [PATCH 32/76] chore: release latest on next --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f23e7bcb..c2f94a260 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: ${{ inputs.image }} - name: Publish latest - if: ${{ github.ref_name == 'main' }} + if: ${{ github.ref_name == 'next' }} run: | docker buildx imagetools create \ --tag ${{ inputs.image_name }}:latest \ From ba2b7e584e77bf3e81c0e067341acaf068c1616e Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 24 Jul 2023 16:41:28 +0200 Subject: [PATCH 33/76] chore: remove dry run --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2f94a260..be673af02 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: uses: ./.github/workflows/version.yml with: semantic_version: ${{ inputs.semantic_version }} - dry_run: true + dry_run: false docker: runs-on: ubuntu-22.04 From 2bdc5df85f8df81403d38ecc2483753ccf1682ed Mon Sep 17 00:00:00 2001 From: Fabi Date: Mon, 24 Jul 2023 16:28:56 +0200 Subject: [PATCH 34/76] docs: add version to technical advisory (#6263) (cherry picked from commit 65f948daec1b293a985dde57a3698309422b0203) --- docs/docs/support/technical_advisory.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/support/technical_advisory.mdx b/docs/docs/support/technical_advisory.mdx index 7fba13420..c9a79dbac 100644 --- a/docs/docs/support/technical_advisory.mdx +++ b/docs/docs/support/technical_advisory.mdx @@ -23,8 +23,8 @@ We understand that these advisories may include breaking changes, and we aim to Reusing user session Breaking Behaviour Change The default behavior for users logging in is to be directed to the Select Account Page on the Login. With the upcoming changes, users will be automatically authenticated when logging into a second application, as long as they only have one active session. No action is required on your part if this is the intended behavior. - TBD - TBD + 2.32.0 + Calendar week 32 From 03ad67775edc343920c204cc1c378c46f4a296ff Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Tue, 25 Jul 2023 07:12:13 +0200 Subject: [PATCH 35/76] fix: upper ZITADEL banner (#6246) Co-authored-by: Livio Spring (cherry picked from commit 57d6113e2aa8ab00e944bdf404e8fafcc0985c4a) --- cmd/start/start.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/start/start.go b/cmd/start/start.go index b3d24cd2f..80f9dc530 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -461,7 +461,7 @@ func shutdownServer(ctx context.Context, server *http.Server) error { } func showBasicInformation(startConfig *Config) { - fmt.Println(color.MagentaString(figure.NewFigure("Zitadel", "", true).String())) + fmt.Println(color.MagentaString(figure.NewFigure("ZITADEL", "", true).String())) http := "http" if startConfig.TLS.Enabled || startConfig.ExternalSecure { http = "https" From c46afa437e6ce77fd8c66ae1aa241b451e77841e Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 25 Jul 2023 07:52:59 +0200 Subject: [PATCH 36/76] fix: build css for login correctly (#6265) (cherry picked from commit 2183b9a7c57b1f027411b5f54d63ca52c2d9ce4c) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index dcb0e2b1f..a11884654 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,8 @@ core_dependencies: .PHONY: core_static core_static: go install github.com/rakyll/statik@v0.1.7 - go generate internal/api/ui/login/statik/generate.go go generate internal/api/ui/login/static/resources/generate.go + go generate internal/api/ui/login/statik/generate.go go generate internal/notification/statik/generate.go go generate internal/statik/generate.go @@ -109,4 +109,4 @@ core_lint: --timeout 10m \ --config ./.golangci.yaml \ --out-format=github-actions \ - --concurrency=$$(getconf _NPROCESSORS_ONLN) \ No newline at end of file + --concurrency=$$(getconf _NPROCESSORS_ONLN) From e2c51e8b02d8b562fe5fdfe651d71fe0546af108 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 25 Jul 2023 08:43:06 +0200 Subject: [PATCH 37/76] chore: build release on `next` branch (#6266) (cherry picked from commit 702dfaf3140478e68f33f7f5026eaf549d144928) --- .github/workflows/build.yml | 2 ++ .releaserc.js | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 192beb3d6..d316c378b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,8 @@ on: permissions: contents: write packages: write + issues: write + pull-requests: write jobs: diff --git a/.releaserc.js b/.releaserc.js index 920186b88..e47c761db 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,8 +1,7 @@ module.exports = { branches: [ - { name: "main" }, { name: "next" }, - { name: "rc", prerelease: true }, + { name: "next-rc", prerelease: "rc" } ], plugins: [ "@semantic-release/commit-analyzer", From 9b058702f28b34d61468d1172d8a931b8479fcbe Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Tue, 25 Jul 2023 08:44:47 +0200 Subject: [PATCH 38/76] fix: remove logstore beta warning (#6244) Co-authored-by: Livio Spring (cherry picked from commit 35f4f74b0850fd596a7d60766d538b19ac88afc4) --- cmd/start/start.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/start/start.go b/cmd/start/start.go index 80f9dc530..49cd981e5 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -218,9 +218,6 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error usageReporter := logstore.UsageReporterFunc(commands.ReportQuotaUsage) actionsLogstoreSvc := logstore.New(queries, usageReporter, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter) - if actionsLogstoreSvc.Enabled() { - logging.Warn("execution logs are currently in beta") - } actions.SetLogstoreService(actionsLogstoreSvc) notification.Start(ctx, config.Projections.Customizations["notifications"], config.Projections.Customizations["notificationsquotas"], config.Projections.Customizations["telemetry"], *config.Telemetry, config.ExternalDomain, config.ExternalPort, config.ExternalSecure, commands, queries, eventstoreClient, assets.AssetAPIFromDomain(config.ExternalSecure, config.ExternalPort), config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS) @@ -314,9 +311,6 @@ func startAPIs( } accessSvc := logstore.New(quotaQuerier, usageReporter, accessDBEmitter, accessStdoutEmitter) - if accessSvc.Enabled() { - logging.Warn("access logs are currently in beta") - } exhaustedCookieHandler := http_util.NewCookieHandler( http_util.WithUnsecure(), http_util.WithNonHttpOnly(), From 880896af75c2d18ecfc0278a0ad16ca7e7044263 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Thu, 27 Jul 2023 14:10:19 +0200 Subject: [PATCH 39/76] perf: reduce events read from eventstore (#6280) * fix: events query user * fix: events query user * user events query * fix tests * fix query * cleanup --------- Co-authored-by: Fabienne (cherry picked from commit d3e403f645a3a9bfd3254b46bb7427b8716542d6) --- .../eventsourcing/eventstore/auth_request.go | 29 +++++++++- .../eventstore/auth_request_test.go | 4 +- .../eventsourcing/eventstore/refresh_token.go | 6 +- .../eventsourcing/eventstore/token.go | 6 +- .../eventsourcing/eventstore/user.go | 8 +-- .../repository/eventsourcing/handler/user.go | 4 +- .../repository/eventsourcing/view/user.go | 2 +- .../eventstore/token_verifier.go | 6 +- .../repository/view/model/refresh_token.go | 11 ++++ internal/user/repository/view/model/token.go | 17 ++++++ internal/user/repository/view/model/user.go | 56 +++++++++++++++++++ .../repository/view/model/user_session.go | 34 +++++++++++ internal/user/repository/view/query.go | 3 +- 13 files changed, 164 insertions(+), 22 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 09c64ba27..c37dd8a79 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -89,7 +89,7 @@ type idpUserLinksProvider interface { } type userEventProvider interface { - UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) + UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []es_models.EventType) ([]*es_models.Event, error) } type userCommandProvider interface { @@ -1314,6 +1314,29 @@ func userSessionsByUserAgentID(provider userSessionViewProvider, agentID, instan return user_view_model.UserSessionsToModel(session), nil } +var ( + userSessionEventTypes = []es_models.EventType{ + es_models.EventType(user_repo.UserV1PasswordCheckSucceededType), + es_models.EventType(user_repo.UserV1PasswordCheckFailedType), + es_models.EventType(user_repo.UserV1MFAOTPCheckSucceededType), + es_models.EventType(user_repo.UserV1MFAOTPCheckFailedType), + es_models.EventType(user_repo.UserV1SignedOutType), + es_models.EventType(user_repo.UserLockedType), + es_models.EventType(user_repo.UserDeactivatedType), + es_models.EventType(user_repo.HumanPasswordCheckSucceededType), + es_models.EventType(user_repo.HumanPasswordCheckFailedType), + es_models.EventType(user_repo.UserIDPLoginCheckSucceededType), + es_models.EventType(user_repo.HumanMFAOTPCheckSucceededType), + es_models.EventType(user_repo.HumanMFAOTPCheckFailedType), + es_models.EventType(user_repo.HumanSignedOutType), + es_models.EventType(user_repo.HumanPasswordlessTokenCheckSucceededType), + es_models.EventType(user_repo.HumanPasswordlessTokenCheckFailedType), + es_models.EventType(user_repo.HumanU2FTokenCheckSucceededType), + es_models.EventType(user_repo.HumanU2FTokenCheckFailedType), + es_models.EventType(user_repo.UserRemovedType), + } +) + func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eventProvider userEventProvider, agentID string, user *user_model.UserView) (*user_model.UserSessionView, error) { instanceID := authz.GetInstance(ctx).InstanceID() session, err := provider.UserSessionByIDs(agentID, user.ID, instanceID) @@ -1330,7 +1353,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve session.Sequence = sequence.CurrentSequence } } - events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence) + events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence, append(session.EventTypes(), userSessionEventTypes...)) if err != nil { logging.WithFields("traceID", tracing.TraceIDFromCtx(ctx)).WithError(err).Debug("error retrieving new events") return user_view_model.UserSessionToModel(session), nil @@ -1411,7 +1434,7 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider } else if user == nil { user = new(user_view_model.UserView) } - events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence) + events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence, user.EventTypes()) if err != nil { logging.WithFields("traceID", tracing.TraceIDFromCtx(ctx)).WithError(err).Debug("error retrieving new events") return user_view_model.UserToModel(user), nil diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index bdcc01213..29737757b 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -105,7 +105,7 @@ type mockEventUser struct { Event *es_models.Event } -func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) { +func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []es_models.EventType) ([]*es_models.Event, error) { events := make([]*es_models.Event, 0) if m.Event != nil { events = append(events, m.Event) @@ -119,7 +119,7 @@ func (m *mockEventUser) BulkAddExternalIDPs(ctx context.Context, userID string, type mockEventErrUser struct{} -func (m *mockEventErrUser) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) { +func (m *mockEventErrUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []es_models.EventType) ([]*es_models.Event, error) { return nil, errors.ThrowInternal(nil, "id", "internal error") } diff --git a/internal/auth/repository/eventsourcing/eventstore/refresh_token.go b/internal/auth/repository/eventsourcing/eventstore/refresh_token.go index 9edc5af15..7d0c1e7d7 100644 --- a/internal/auth/repository/eventsourcing/eventstore/refresh_token.go +++ b/internal/auth/repository/eventsourcing/eventstore/refresh_token.go @@ -62,7 +62,7 @@ func (r *RefreshTokenRepo) RefreshTokenByID(ctx context.Context, tokenID, userID } } - events, esErr := r.getUserEvents(ctx, userID, tokenView.InstanceID, tokenView.Sequence) + events, esErr := r.getUserEvents(ctx, userID, tokenView.InstanceID, tokenView.Sequence, tokenView.GetRelevantEventTypes()) if errors.IsNotFound(viewErr) && len(events) == 0 { return nil, errors.ThrowNotFound(nil, "EVENT-BHB52", "Errors.User.RefreshToken.Invalid") } @@ -106,8 +106,8 @@ func (r *RefreshTokenRepo) SearchMyRefreshTokens(ctx context.Context, userID str }, nil } -func (r *RefreshTokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64) ([]*models.Event, error) { - query, err := usr_view.UserByIDQuery(userID, instanceID, sequence) +func (r *RefreshTokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { + query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } diff --git a/internal/auth/repository/eventsourcing/eventstore/token.go b/internal/auth/repository/eventsourcing/eventstore/token.go index 7894a6f4c..151c25e6b 100644 --- a/internal/auth/repository/eventsourcing/eventstore/token.go +++ b/internal/auth/repository/eventsourcing/eventstore/token.go @@ -55,7 +55,7 @@ func (repo *TokenRepo) TokenByIDs(ctx context.Context, userID, tokenID string) ( } } - events, esErr := repo.getUserEvents(ctx, userID, token.InstanceID, token.Sequence) + events, esErr := repo.getUserEvents(ctx, userID, token.InstanceID, token.Sequence, token.GetRelevantEventTypes()) if errors.IsNotFound(viewErr) && len(events) == 0 { return nil, errors.ThrowNotFound(nil, "EVENT-4T90g", "Errors.Token.NotFound") } @@ -77,8 +77,8 @@ func (repo *TokenRepo) TokenByIDs(ctx context.Context, userID, tokenID string) ( return model.TokenViewToModel(token), nil } -func (r *TokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64) ([]*models.Event, error) { - query, err := usr_view.UserByIDQuery(userID, instanceID, sequence) +func (r *TokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { + query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 2370e1c1b..cebf0d474 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -39,12 +39,12 @@ func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID s return userIDs, nil } -func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*models.Event, error) { - return repo.getUserEvents(ctx, id, sequence) +func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { + return repo.getUserEvents(ctx, id, sequence, eventTypes) } -func (r *UserRepo) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) { - query, err := usr_view.UserByIDQuery(userID, authz.GetInstance(ctx).InstanceID(), sequence) +func (r *UserRepo) getUserEvents(ctx context.Context, userID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { + query, err := usr_view.UserByIDQuery(userID, authz.GetInstance(ctx).InstanceID(), sequence, eventTypes) if err != nil { return nil, err } diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go index b226f54b1..847f483ea 100644 --- a/internal/auth/repository/eventsourcing/handler/user.go +++ b/internal/auth/repository/eventsourcing/handler/user.go @@ -163,7 +163,7 @@ func (u *User) ProcessUser(event *es_models.Event) (err error) { "userID", event.AggregateID, "eventType", event.Type, ).Info("user not found in view") - query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0) + query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0, user.EventTypes()) if err != nil { return err } @@ -191,7 +191,7 @@ func (u *User) ProcessUser(event *es_models.Event) (err error) { "userID", event.AggregateID, "eventType", event.Type, ).Info("user not found in view") - query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0) + query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0, user.EventTypes()) if err != nil { return err } diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index 6a82f6ed6..b2008244a 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -108,7 +108,7 @@ func (v *View) userByID(ctx context.Context, instanceID string, queries ...query } } - query, err := view.UserByIDQuery(queriedUser.ID, instanceID, user.Sequence) + query, err := view.UserByIDQuery(queriedUser.ID, instanceID, user.Sequence, user.EventTypes()) if err != nil { return nil, err } diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index 3fc61c854..5bcecf9dd 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -63,7 +63,7 @@ func (repo *TokenVerifierRepo) tokenByID(ctx context.Context, tokenID, userID st } } - events, esErr := repo.getUserEvents(ctx, userID, instanceID, token.Sequence) + events, esErr := repo.getUserEvents(ctx, userID, instanceID, token.Sequence, token.GetRelevantEventTypes()) if caos_errs.IsNotFound(viewErr) && len(events) == 0 { return nil, caos_errs.ThrowNotFound(nil, "EVENT-4T90g", "Errors.Token.NotFound") } @@ -238,10 +238,10 @@ func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName str return clientID, app.ProjectID, nil } -func (repo *TokenVerifierRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64) (_ []*models.Event, err error) { +func (repo *TokenVerifierRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) (_ []*models.Event, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - query, err := usr_view.UserByIDQuery(userID, instanceID, sequence) + query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } diff --git a/internal/user/repository/view/model/refresh_token.go b/internal/user/repository/view/model/refresh_token.go index a0aee58bc..a85aea06a 100644 --- a/internal/user/repository/view/model/refresh_token.go +++ b/internal/user/repository/view/model/refresh_token.go @@ -155,3 +155,14 @@ func (t *RefreshTokenView) appendRenewedEvent(event *es_models.Event) error { func (t *RefreshTokenView) appendRemovedEvent(event *es_models.Event) { t.Expiration = event.CreationDate } + +func (t *RefreshTokenView) GetRelevantEventTypes() []es_models.EventType { + return []es_models.EventType{ + es_models.EventType(user_repo.HumanRefreshTokenAddedType), + es_models.EventType(user_repo.HumanRefreshTokenRenewedType), + es_models.EventType(user_repo.HumanRefreshTokenRemovedType), + es_models.EventType(user_repo.UserRemovedType), + es_models.EventType(user_repo.UserDeactivatedType), + es_models.EventType(user_repo.UserLockedType), + } +} diff --git a/internal/user/repository/view/model/token.go b/internal/user/repository/view/model/token.go index e0b80a192..df9d12871 100644 --- a/internal/user/repository/view/model/token.go +++ b/internal/user/repository/view/model/token.go @@ -182,6 +182,23 @@ func (t *TokenView) appendPATRemoved(event *es_models.Event) error { return nil } +func (t *TokenView) GetRelevantEventTypes() []es_models.EventType { + return []es_models.EventType{ + es_models.EventType(user_repo.UserTokenAddedType), + es_models.EventType(user_repo.PersonalAccessTokenAddedType), + es_models.EventType(user_repo.UserTokenRemovedType), + es_models.EventType(user_repo.HumanRefreshTokenRemovedType), + es_models.EventType(user_repo.UserV1SignedOutType), + es_models.EventType(user_repo.HumanSignedOutType), + es_models.EventType(user_repo.UserRemovedType), + es_models.EventType(user_repo.UserDeactivatedType), + es_models.EventType(user_repo.UserLockedType), + es_models.EventType(user_repo.UserLockedType), + es_models.EventType(user_repo.UserReactivatedType), + es_models.EventType(user_repo.PersonalAccessTokenRemovedType), + } +} + func eventToMap(event *es_models.Event) (map[string]interface{}, error) { m := make(map[string]interface{}) if err := json.Unmarshal(event.Data, &m); err != nil { diff --git a/internal/user/repository/view/model/user.go b/internal/user/repository/view/model/user.go index 4c68711a0..d5d913478 100644 --- a/internal/user/repository/view/model/user.go +++ b/internal/user/repository/view/model/user.go @@ -534,3 +534,59 @@ func (u *UserView) SetEmptyUserType() { u.HumanView = nil } } + +func (u *UserView) EventTypes() []models.EventType { + return []models.EventType{ + models.EventType(user.MachineAddedEventType), + models.EventType(user.UserV1AddedType), + models.EventType(user.UserV1RegisteredType), + models.EventType(user.HumanRegisteredType), + models.EventType(user.HumanAddedType), + models.EventType(user.UserRemovedType), + models.EventType(user.UserV1PasswordChangedType), + models.EventType(user.HumanPasswordChangedType), + models.EventType(user.HumanPasswordlessTokenAddedType), + models.EventType(user.HumanPasswordlessTokenVerifiedType), + models.EventType(user.HumanPasswordlessTokenRemovedType), + models.EventType(user.UserV1ProfileChangedType), + models.EventType(user.HumanProfileChangedType), + models.EventType(user.UserV1AddressChangedType), + models.EventType(user.HumanAddressChangedType), + models.EventType(user.MachineChangedEventType), + models.EventType(user.UserDomainClaimedType), + models.EventType(user.UserUserNameChangedType), + models.EventType(user.UserV1EmailChangedType), + models.EventType(user.HumanEmailChangedType), + models.EventType(user.UserV1EmailVerifiedType), + models.EventType(user.HumanEmailVerifiedType), + models.EventType(user.UserV1PhoneChangedType), + models.EventType(user.HumanPhoneChangedType), + models.EventType(user.UserV1PhoneVerifiedType), + models.EventType(user.HumanPhoneVerifiedType), + models.EventType(user.UserV1PhoneRemovedType), + models.EventType(user.HumanPhoneRemovedType), + models.EventType(user.UserDeactivatedType), + models.EventType(user.UserReactivatedType), + models.EventType(user.UserUnlockedType), + models.EventType(user.UserLockedType), + models.EventType(user.UserV1MFAOTPAddedType), + models.EventType(user.HumanMFAOTPAddedType), + models.EventType(user.UserV1MFAOTPVerifiedType), + models.EventType(user.HumanMFAOTPVerifiedType), + models.EventType(user.UserV1MFAOTPRemovedType), + models.EventType(user.HumanMFAOTPRemovedType), + models.EventType(user.HumanU2FTokenAddedType), + models.EventType(user.HumanU2FTokenVerifiedType), + models.EventType(user.HumanU2FTokenRemovedType), + models.EventType(user.UserV1MFAInitSkippedType), + models.EventType(user.HumanMFAInitSkippedType), + models.EventType(user.UserV1InitialCodeAddedType), + models.EventType(user.HumanInitialCodeAddedType), + models.EventType(user.UserV1InitializedCheckSucceededType), + models.EventType(user.HumanInitializedCheckSucceededType), + models.EventType(user.HumanAvatarAddedType), + models.EventType(user.HumanAvatarRemovedType), + models.EventType(user.HumanPasswordlessInitCodeAddedType), + models.EventType(user.HumanPasswordlessInitCodeRequestedType), + } +} diff --git a/internal/user/repository/view/model/user_session.go b/internal/user/repository/view/model/user_session.go index 98689590c..b86076177 100644 --- a/internal/user/repository/view/model/user_session.go +++ b/internal/user/repository/view/model/user_session.go @@ -198,3 +198,37 @@ func avatarKeyFromEvent(event *models.Event) (string, error) { } return data["storeKey"], nil } + +func (v *UserSessionView) EventTypes() []models.EventType { + return []models.EventType{ + models.EventType(user.UserV1PasswordCheckSucceededType), + models.EventType(user.HumanPasswordCheckSucceededType), + models.EventType(user.UserIDPLoginCheckSucceededType), + models.EventType(user.HumanPasswordlessTokenCheckSucceededType), + models.EventType(user.HumanPasswordlessTokenCheckFailedType), + models.EventType(user.HumanPasswordlessTokenRemovedType), + models.EventType(user.UserV1PasswordCheckFailedType), + models.EventType(user.HumanPasswordCheckFailedType), + models.EventType(user.UserV1PasswordChangedType), + models.EventType(user.HumanPasswordChangedType), + models.EventType(user.HumanMFAOTPVerifiedType), + models.EventType(user.UserV1MFAOTPCheckSucceededType), + models.EventType(user.HumanMFAOTPCheckSucceededType), + models.EventType(user.UserV1MFAOTPCheckFailedType), + models.EventType(user.UserV1MFAOTPRemovedType), + models.EventType(user.HumanMFAOTPCheckFailedType), + models.EventType(user.HumanMFAOTPRemovedType), + models.EventType(user.HumanU2FTokenCheckFailedType), + models.EventType(user.HumanU2FTokenRemovedType), + models.EventType(user.HumanU2FTokenVerifiedType), + models.EventType(user.HumanU2FTokenCheckSucceededType), + models.EventType(user.UserV1SignedOutType), + models.EventType(user.HumanSignedOutType), + models.EventType(user.UserLockedType), + models.EventType(user.UserDeactivatedType), + models.EventType(user.UserIDPLinkRemovedType), + models.EventType(user.UserIDPLinkCascadeRemovedType), + models.EventType(user.HumanAvatarAddedType), + models.EventType(user.HumanAvatarRemovedType), + } +} diff --git a/internal/user/repository/view/query.go b/internal/user/repository/view/query.go index 594c99925..c35188a22 100644 --- a/internal/user/repository/view/query.go +++ b/internal/user/repository/view/query.go @@ -6,7 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/repository/user" ) -func UserByIDQuery(id, instanceID string, latestSequence uint64) (*es_models.SearchQuery, error) { +func UserByIDQuery(id, instanceID string, latestSequence uint64, eventTypes []es_models.EventType) (*es_models.SearchQuery, error) { if id == "" { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d8isw", "Errors.User.UserIDMissing") } @@ -14,6 +14,7 @@ func UserByIDQuery(id, instanceID string, latestSequence uint64) (*es_models.Sea AddQuery(). AggregateTypeFilter(user.AggregateType). AggregateIDFilter(id). + EventTypesFilter(eventTypes...). LatestSequenceFilter(latestSequence). InstanceIDFilter(instanceID). SearchQuery(), nil From 9d2ae1e9fad19596c9fa7ef14025cad1b5cb0395 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 28 Jul 2023 09:09:15 +0200 Subject: [PATCH 40/76] fix: password hash update and add missing i18n (#6285) (cherry picked from commit 789dcd86154b216fcd524b9c715e936893bcdb63) --- internal/api/ui/login/static/i18n/bg.yaml | 1 + internal/api/ui/login/static/i18n/de.yaml | 1 + internal/api/ui/login/static/i18n/en.yaml | 1 + internal/api/ui/login/static/i18n/es.yaml | 1 + internal/api/ui/login/static/i18n/fr.yaml | 1 + internal/api/ui/login/static/i18n/it.yaml | 1 + internal/api/ui/login/static/i18n/ja.yaml | 1 + internal/api/ui/login/static/i18n/mk.yaml | 1 + internal/api/ui/login/static/i18n/pl.yaml | 1 + internal/api/ui/login/static/i18n/pt.yaml | 1 + internal/api/ui/login/static/i18n/zh.yaml | 1 + internal/command/user_human_password_model.go | 3 ++- internal/query/user_password.go | 3 ++- internal/repository/user/eventstore.go | 2 +- internal/repository/user/human_password.go | 3 ++- internal/repository/user/v1.go | 1 - internal/static/i18n/bg.yaml | 4 ++++ internal/static/i18n/de.yaml | 4 ++++ internal/static/i18n/en.yaml | 4 ++++ internal/static/i18n/es.yaml | 4 ++++ internal/static/i18n/fr.yaml | 4 ++++ internal/static/i18n/it.yaml | 4 ++++ internal/static/i18n/ja.yaml | 4 ++++ internal/static/i18n/mk.yaml | 4 ++++ internal/static/i18n/pl.yaml | 4 ++++ internal/static/i18n/pt.yaml | 5 +++++ internal/static/i18n/zh.yaml | 4 ++++ 27 files changed, 63 insertions(+), 5 deletions(-) diff --git a/internal/api/ui/login/static/i18n/bg.yaml b/internal/api/ui/login/static/i18n/bg.yaml index 20b1c1084..030e96fc2 100644 --- a/internal/api/ui/login/static/i18n/bg.yaml +++ b/internal/api/ui/login/static/i18n/bg.yaml @@ -391,6 +391,7 @@ Errors: InvalidAndLocked: >- Паролата е невалидна и потребителят е заключен, свържете се с вашия администратор. + NotChanged: Паролата не е променена UsernameOrPassword: Invalid: Потребителското име или паролата са невалидни PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index 984580359..df9cf77b6 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -399,6 +399,7 @@ Errors: Empty: Passwort ist leer Invalid: Passwort ungültig InvalidAndLocked: Password ist ungültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator. + NotChanged: Passwort nicht geändert UsernameOrPassword: Invalid: Username oder Passwort ist ungültig PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index ceb60654e..3e06b634f 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -399,6 +399,7 @@ Errors: Empty: Password is empty Invalid: Password is invalid InvalidAndLocked: Password is invalid and user is locked, contact your administrator. + NotChanged: Password not changed UsernameOrPassword: Invalid: Username or Password is invalid PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml index 7adf48d53..241ea2d93 100644 --- a/internal/api/ui/login/static/i18n/es.yaml +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -381,6 +381,7 @@ Errors: Empty: La contraseña está vacía Invalid: La contraseña no es válida InvalidAndLocked: La contraseña no es válida y el usuario está bloqueado, contacta con tu administrador. + NotChanged: Contraseña no modificada UsernameOrPassword: Invalid: El nombre de usuario o la contraseña no son válidos PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index 7cd2b0ad8..45e62770d 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -399,6 +399,7 @@ Errors: Empty: Le mot de passe est vide Invalid: Le mot de passe n'est pas valide InvalidAndLocked: Le mot de passe n'est pas valide et l'utilisateur est verrouillé, contactez votre administrateur. + NotChanged: Mot de passe non modifié UsernameOrPassword: Invalid: Le nom d'utilisateur ou le mot de passe n'est pas valide PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index 8a30e9f97..3aa6c5c54 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -399,6 +399,7 @@ Errors: Empty: La password è vuota Invalid: La password non è valida InvalidAndLocked: La password non è valida e l'utente è bloccato, contatta il tuo amministratore. + NotChanged: Password non modificata UsernameOrPassword: Invalid: Il nome utente o la password non sono validi PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index 599710131..9db77893c 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -362,6 +362,7 @@ Errors: Empty: パスワードが空です Invalid: 無効なパスワードです InvalidAndLocked: パスワードが無効かつユーザーがロックされているため、管理者に連絡してください。 + NotChanged: パスワードは変更されていません UsernameOrPassword: Invalid: ユーザー名またはパスワードは無効です PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/mk.yaml b/internal/api/ui/login/static/i18n/mk.yaml index d17f2fdbb..9f6dc6fc5 100644 --- a/internal/api/ui/login/static/i18n/mk.yaml +++ b/internal/api/ui/login/static/i18n/mk.yaml @@ -401,6 +401,7 @@ Errors: Empty: Лозинката е празна Invalid: Лозинката не е валидна InvalidAndLocked: Лозинката не е валидна и корисникот е заклучен, контактирајте со вашиот администратор. + NotChanged: Лозинката не е променета UsernameOrPassword: Invalid: Корисничкото име и/или лозинката не се валидни PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index 56d4cc683..e4046b95f 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -399,6 +399,7 @@ Errors: Empty: Hasło jest puste Invalid: Hasło jest niepoprawne InvalidAndLocked: Hasło jest niepoprawne i użytkownik jest zablokowany, skontaktuj się z administratorem. + NotChanged: Hasło nie zostało zmienione UsernameOrPassword: Invalid: Nazwa użytkownika lub hasło jest niepoprawne PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/pt.yaml b/internal/api/ui/login/static/i18n/pt.yaml index e887ff6bc..cba0948f7 100644 --- a/internal/api/ui/login/static/i18n/pt.yaml +++ b/internal/api/ui/login/static/i18n/pt.yaml @@ -397,6 +397,7 @@ Errors: Empty: A senha está vazia Invalid: A senha é inválida InvalidAndLocked: A senha é inválida e o usuário está bloqueado, entre em contato com o administrador. + NotChanged: Senha não alterada UsernameOrPassword: Invalid: Nome de usuário ou senha inválidos PasswordComplexityPolicy: diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index 0680fa267..eb27f0de2 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -399,6 +399,7 @@ Errors: Empty: 密码为空 Invalid: 密码无效 InvalidAndLocked: 密码无效且用户被锁定,请联系您的管理员。 + NotChanged: 密码未更改 UsernameOrPassword: Invalid: 用户名或密码无效 PasswordComplexityPolicy: diff --git a/internal/command/user_human_password_model.go b/internal/command/user_human_password_model.go index c0ca58f58..c4064ba15 100644 --- a/internal/command/user_human_password_model.go +++ b/internal/command/user_human_password_model.go @@ -90,6 +90,7 @@ func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder { user.HumanEmailVerifiedType, user.HumanPasswordCheckFailedType, user.HumanPasswordCheckSucceededType, + user.HumanPasswordHashUpdatedType, user.UserRemovedType, user.UserUnlockedType, user.UserV1AddedType, @@ -101,7 +102,7 @@ func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder { user.UserV1EmailVerifiedType, user.UserV1PasswordCheckFailedType, user.UserV1PasswordCheckSucceededType, - user.UserV1PasswordHashUpdatedType). + ). Builder() if wm.ResourceOwner != "" { diff --git a/internal/query/user_password.go b/internal/query/user_password.go index 97ca99541..9d8ab7140 100644 --- a/internal/query/user_password.go +++ b/internal/query/user_password.go @@ -126,6 +126,7 @@ func (wm *HumanPasswordReadModel) Query() *eventstore.SearchQueryBuilder { user.HumanEmailVerifiedType, user.HumanPasswordCheckFailedType, user.HumanPasswordCheckSucceededType, + user.HumanPasswordHashUpdatedType, user.UserRemovedType, user.UserUnlockedType, user.UserV1AddedType, @@ -137,7 +138,7 @@ func (wm *HumanPasswordReadModel) Query() *eventstore.SearchQueryBuilder { user.UserV1EmailVerifiedType, user.UserV1PasswordCheckFailedType, user.UserV1PasswordCheckSucceededType, - user.UserV1PasswordHashUpdatedType). + ). Builder() if wm.ResourceOwner != "" { diff --git a/internal/repository/user/eventstore.go b/internal/repository/user/eventstore.go index 847736b60..1505c31cf 100644 --- a/internal/repository/user/eventstore.go +++ b/internal/repository/user/eventstore.go @@ -17,7 +17,6 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, UserV1PasswordCodeSentType, HumanPasswordCodeSentEventMapper). RegisterFilterEventMapper(AggregateType, UserV1PasswordCheckSucceededType, HumanPasswordCheckSucceededEventMapper). RegisterFilterEventMapper(AggregateType, UserV1PasswordCheckFailedType, HumanPasswordCheckFailedEventMapper). - RegisterFilterEventMapper(AggregateType, UserV1PasswordHashUpdatedType, eventstore.GenericEventMapper[HumanPasswordHashUpdatedEvent]). RegisterFilterEventMapper(AggregateType, UserV1EmailChangedType, HumanEmailChangedEventMapper). RegisterFilterEventMapper(AggregateType, UserV1EmailVerifiedType, HumanEmailVerifiedEventMapper). RegisterFilterEventMapper(AggregateType, UserV1EmailVerificationFailedType, HumanEmailVerificationFailedEventMapper). @@ -63,6 +62,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, HumanPasswordChangeSentType, HumanPasswordChangeSentEventMapper). RegisterFilterEventMapper(AggregateType, HumanPasswordCheckSucceededType, HumanPasswordCheckSucceededEventMapper). RegisterFilterEventMapper(AggregateType, HumanPasswordCheckFailedType, HumanPasswordCheckFailedEventMapper). + RegisterFilterEventMapper(AggregateType, HumanPasswordHashUpdatedType, eventstore.GenericEventMapper[HumanPasswordHashUpdatedEvent]). RegisterFilterEventMapper(AggregateType, UserIDPLinkAddedType, UserIDPLinkAddedEventMapper). RegisterFilterEventMapper(AggregateType, UserIDPLinkRemovedType, UserIDPLinkRemovedEventMapper). RegisterFilterEventMapper(AggregateType, UserIDPLinkCascadeRemovedType, UserIDPLinkCascadeRemovedEventMapper). diff --git a/internal/repository/user/human_password.go b/internal/repository/user/human_password.go index f2f40cd6c..909da2426 100644 --- a/internal/repository/user/human_password.go +++ b/internal/repository/user/human_password.go @@ -21,6 +21,7 @@ const ( HumanPasswordCodeSentType = passwordEventPrefix + "code.sent" HumanPasswordCheckSucceededType = passwordEventPrefix + "check.succeeded" HumanPasswordCheckFailedType = passwordEventPrefix + "check.failed" + HumanPasswordHashUpdatedType = passwordEventPrefix + "hash.updated" ) type HumanPasswordChangedEvent struct { @@ -298,7 +299,7 @@ func NewHumanPasswordHashUpdatedEvent( BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, - HumanPasswordCheckFailedType, + HumanPasswordHashUpdatedType, ), EncodedHash: encoded, } diff --git a/internal/repository/user/v1.go b/internal/repository/user/v1.go index a2974bad9..010e1a235 100644 --- a/internal/repository/user/v1.go +++ b/internal/repository/user/v1.go @@ -15,7 +15,6 @@ const ( UserV1PasswordCodeSentType = userV1PasswordEventTypePrefix + "code.sent" UserV1PasswordCheckSucceededType = userV1PasswordEventTypePrefix + "check.succeeded" UserV1PasswordCheckFailedType = userV1PasswordEventTypePrefix + "check.failed" - UserV1PasswordHashUpdatedType = userV1PasswordEventTypePrefix + "hash.updated" userV1EmailEventTypePrefix = userEventTypePrefix + "email." UserV1EmailChangedType = userV1EmailEventTypePrefix + "changed" diff --git a/internal/static/i18n/bg.yaml b/internal/static/i18n/bg.yaml index 959c5fbbf..33dd828a7 100644 --- a/internal/static/i18n/bg.yaml +++ b/internal/static/i18n/bg.yaml @@ -682,6 +682,10 @@ EventTypes: check: succeeded: Проверката на паролата е успешна failed: Проверката на паролата е неуспешна + change: + sent: Промяната на паролата е изпратена + hash: + updated: Хешът на паролата е актуализиран phone: changed: Телефонният номер е променен verified: Телефонният номер е потвърден diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 929ce1db6..b485f65c6 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -583,6 +583,10 @@ EventTypes: check: succeeded: Passwortvalidierung erfolgreich failed: Passwortvalidierung fehlgeschlagen + change: + sent: Passwordänderung versendet + hash: + updated: Passwort Hash geändert externallogin: check: succeeded: Externer login erfolgreich durchgeführt diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index f623dc213..93a529045 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -583,6 +583,10 @@ EventTypes: check: succeeded: Password check succeeded failed: Password check failed + change: + sent: Password change sent + hash: + updated: Password hash updated externallogin: check: succeeded: External login succeeded diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index 097bd9e7c..5dc4a3ab4 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -583,6 +583,10 @@ EventTypes: check: succeeded: Comprobación exitosa de la contraseña failed: Fallo en la comprobación de la contraseña + change: + sent: Cambio de contraseña enviado + hash: + updated: Hash de contraseña actualizado externallogin: check: succeeded: Inicio de sesión externo con éxito diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index 7fba4deaf..b80a9ed33 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -581,6 +581,10 @@ EventTypes: check: succeeded: Vérification du mot de passe réussie failed: La vérification du mot de passe a échoué + change: + sent: Changement de mot de passe envoyé + hash: + updated: Hachage du mot de passe mis à jour externallogin: check: succeeded: Connexion externe réussie diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 4981dc307..e1bd873bb 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -581,6 +581,10 @@ EventTypes: check: succeeded: Controllo della password riuscito failed: Controllo della password fallito + change: + sent: Cambio password inviato + hash: + updated: Hash della password aggiornato externallogin: check: succeeded: Accesso esterno riuscito diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index d0ba74e27..eadf7ad5a 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -655,6 +655,10 @@ EventTypes: check: succeeded: パスワードチェックの成功 failed: パスワードチェックの失敗 + change: + sent: パスワード変更を送信しました + hash: + updated: パスワードハッシュが更新されました phone: changed: 電話番号の変更 verified: 電話番号の検証 diff --git a/internal/static/i18n/mk.yaml b/internal/static/i18n/mk.yaml index a02b79dc9..c3fcfdf69 100644 --- a/internal/static/i18n/mk.yaml +++ b/internal/static/i18n/mk.yaml @@ -666,6 +666,10 @@ EventTypes: check: succeeded: Проверката на лозинката е успешна failed: Проверката на лозинката е неуспешна + change: + sent: Промената на лозинката е испратена + hash: + updated: Хашот на лозинката е ажуриран phone: changed: Бројот на телефонот е променет verified: Бројот на телефонот е верифициран diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index 3e48f4582..aa84c8ca5 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -583,6 +583,10 @@ EventTypes: check: succeeded: Sprawdzenie hasła zakończone powodzeniem failed: Sprawdzenie hasła nie powiodło się + change: + sent: Wysłano zmianę hasła + hash: + updated: Zaktualizowano skrót hasła externallogin: check: succeeded: Zewnętrzne logowanie zakończone powodzeniem diff --git a/internal/static/i18n/pt.yaml b/internal/static/i18n/pt.yaml index 963191eb5..4f3c5af85 100644 --- a/internal/static/i18n/pt.yaml +++ b/internal/static/i18n/pt.yaml @@ -120,6 +120,7 @@ Errors: Empty: Senha está vazia Invalid: Senha é inválida NotSet: O usuário não definiu uma senha + NotChanged: Senha não alterada PasswordComplexityPolicy: NotFound: Política de complexidade de senha não encontrada MinLength: A senha é muito curta @@ -660,6 +661,10 @@ EventTypes: check: succeeded: Verificação de senha bem-sucedida failed: Verificação de senha falhou + change: + sent: Alteração de senha enviada + hash: + updated: Hash da senha atualizado phone: changed: Número de telefone alterado verified: Número de telefone verificado diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index 1209fd515..fb068a233 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -664,6 +664,10 @@ EventTypes: check: succeeded: 密码检查成功 failed: 密码检查失败 + change: + sent: 密码更改已发送 + hash: + updated: 密码哈希已更新 phone: changed: 更改手机号码 verified: 验证手机号码 From 993808cbcb161ae7b2986261aaf52380549f219c Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 28 Jul 2023 09:58:05 +0200 Subject: [PATCH 41/76] fix(i18n): do not translate language itself (#6286) (cherry picked from commit 43cb62ca4ea5c7e3523ec2a0991335a7e52dc785) --- CONTRIBUTING.md | 1 + internal/api/ui/login/static/i18n/bg.yaml | 22 +++++++++++++--------- internal/api/ui/login/static/i18n/de.yaml | 4 ++++ internal/api/ui/login/static/i18n/en.yaml | 6 +++++- internal/api/ui/login/static/i18n/es.yaml | 6 +++++- internal/api/ui/login/static/i18n/fr.yaml | 4 ++++ internal/api/ui/login/static/i18n/it.yaml | 4 ++++ internal/api/ui/login/static/i18n/ja.yaml | 4 ++++ internal/api/ui/login/static/i18n/mk.yaml | 4 +++- internal/api/ui/login/static/i18n/pl.yaml | 4 ++++ internal/api/ui/login/static/i18n/pt.yaml | 16 ++++++++-------- internal/api/ui/login/static/i18n/zh.yaml | 4 ++++ 12 files changed, 59 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5884c559b..2b04c7d36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -375,6 +375,7 @@ ZITADEL loads translations from four files: - [Common texts](./internal/static/i18n) for success or error toasts You may edit the texts in these files or create a new file for additional language support. Make sure you set the locale (ISO 639-1 code) as the name of the new language file. +Please make sure that the languages within the files remain in their own language, e.g. German must always be `Deutsch. ## Want to start ZITADEL? diff --git a/internal/api/ui/login/static/i18n/bg.yaml b/internal/api/ui/login/static/i18n/bg.yaml index 030e96fc2..33e963c63 100644 --- a/internal/api/ui/login/static/i18n/bg.yaml +++ b/internal/api/ui/login/static/i18n/bg.yaml @@ -213,11 +213,11 @@ RegistrationUser: LastnameLabel: Фамилия LanguageLabel: език German: Deutsch - English: Английски - Italian: Италиано + English: English + Italian: Italiano French: Français Chinese: 简体中文 - Polish: Полски + Polish: Polski Japanese: 日本語 Spanish: Español Bulgarian: Български @@ -248,13 +248,15 @@ ExternalRegistrationUserOverview: PhoneLabel: Телефонен номер LanguageLabel: език German: Deutsch - English: Английски - Italian: Италиано + English: English + Italian: Italiano French: Français Chinese: 简体中文 + Polish: Polski Japanese: 日本語 - Polish: Полски Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Правила и условия TosConfirm: Приемам @@ -305,13 +307,15 @@ ExternalNotFound: PrivacyConfirm: Приемам PrivacyLinkText: политика за поверителност German: Deutsch - English: Английски - Italian: Италиано + English: English + Italian: Italiano French: Français Chinese: 简体中文 - Polish: Полски + Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index df9cf77b6..490d9ebc0 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -258,6 +258,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz TosConfirm: Ich akzeptiere die @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index 3e06b634f..76c7e0ff0 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -222,6 +222,7 @@ RegistrationUser: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български Portuguese: Português Macedonian: Македонски GenderLabel: Gender @@ -254,9 +255,11 @@ ExternalRegistrationUserOverview: Italian: Italiano French: Français Chinese: 简体中文 - Japanese: 日本語 Polish: Polski + Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Terms and conditions TosConfirm: I accept the @@ -320,6 +323,7 @@ ExternalNotFound: Japanese: 日本語 Spanish: Español Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml index 241ea2d93..ed98842db 100644 --- a/internal/api/ui/login/static/i18n/es.yaml +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -255,9 +255,11 @@ ExternalRegistrationUserOverview: Italian: Italiano French: Français Chinese: 简体中文 - Japanese: 日本語 Polish: Polski + Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Términos y condiciones TosConfirm: Acepto los @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски Footer: diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index 45e62770d..29db5b3e2 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -258,6 +258,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Termes et conditions TosConfirm: J'accepte les @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index 3aa6c5c54..5e998c2e5 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -258,6 +258,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Termini di servizio TosConfirm: Accetto i @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index 9db77893c..f51a1ca3d 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -250,6 +250,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: 利用規約 TosConfirm: 私は利用規約を承諾します。 @@ -312,6 +314,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/mk.yaml b/internal/api/ui/login/static/i18n/mk.yaml index 9f6dc6fc5..217ab7e2f 100644 --- a/internal/api/ui/login/static/i18n/mk.yaml +++ b/internal/api/ui/login/static/i18n/mk.yaml @@ -222,6 +222,7 @@ RegistrationUser: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български Portuguese: Português Macedonian: Македонски GenderLabel: Пол @@ -254,9 +255,10 @@ ExternalRegistrationUserOverview: Italian: Italiano French: Français Chinese: 简体中文 - Japanese: 日本語 Polish: Polski + Japanese: 日本語 Spanish: Español + Bulgarian: Български Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Правила и услови diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index e4046b95f..8561cec9e 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -258,6 +258,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: Warunki i zasady TosConfirm: Akceptuję @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: diff --git a/internal/api/ui/login/static/i18n/pt.yaml b/internal/api/ui/login/static/i18n/pt.yaml index cba0948f7..42c6c27a0 100644 --- a/internal/api/ui/login/static/i18n/pt.yaml +++ b/internal/api/ui/login/static/i18n/pt.yaml @@ -210,15 +210,15 @@ RegistrationUser: FirstnameLabel: Nome próprio LastnameLabel: Sobrenome LanguageLabel: Idioma - German: Alemão - English: Inglês + German: Deutsch + English: English Italian: Italiano - French: Francês - Chinese: Chinês simplificado - Polish: Polonês - Japanese: Japonês - Spanish: Espanhol - Bulgarian: Búlgaro + French: Français + Chinese: 简体中文 + Polish: Polski + Japanese: 日本語 + Spanish: Español + Bulgarian: Български Portuguese: Português Macedonian: Македонски GenderLabel: Gênero diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index eb27f0de2..204e1a45a 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -258,6 +258,8 @@ ExternalRegistrationUserOverview: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски TosAndPrivacyLabel: 条款和条款 TosConfirm: 我接受 @@ -320,6 +322,8 @@ ExternalNotFound: Polish: Polski Japanese: 日本語 Spanish: Español + Bulgarian: Български + Portuguese: Português Macedonian: Македонски DeviceAuth: From 660a7b2576143dcf2ee3bf384f2caedf0eda0363 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 31 Jul 2023 15:55:26 +0200 Subject: [PATCH 42/76] fix(OIDC): introspection (#6298) * fix(OIDC): introspect for PAT * fix(OIDC): introspect for PAT * fix(OIDC): introspect * remove adding projectID into audience (cherry picked from commit 782f7ad6473bb6c2f7d0cab8dfece93adcdbb230) --- internal/api/oidc/client.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 7a418df9a..09475f1de 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -189,7 +189,7 @@ func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection return errors.ThrowPermissionDenied(nil, "OIDC-Adfg5", "client not found") } return o.introspect(ctx, introspection, - tokenID, token.UserID, token.ClientID, projectID, + tokenID, token.UserID, token.ClientID, clientID, projectID, token.Audience, token.Scope, token.AccessTokenCreation, token.AccessTokenExpiration) } @@ -209,7 +209,7 @@ func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection } } return o.introspect(ctx, introspection, - token.ID, token.UserID, token.ApplicationID, projectID, + token.ID, token.UserID, token.ApplicationID, clientID, projectID, token.Audience, token.Scopes, token.CreationDate, token.Expiration) } @@ -272,7 +272,7 @@ func (o *OPStorage) isOriginAllowed(ctx context.Context, clientID, origin string func (o *OPStorage) introspect( ctx context.Context, introspection *oidc.IntrospectionResponse, - tokenID, subject, clientID, projectID string, + tokenID, subject, tokenClientID, introspectionClientID, introspectionProjectID string, audience, scope []string, tokenCreation, tokenExpiration time.Time, ) (err error) { @@ -280,15 +280,15 @@ func (o *OPStorage) introspect( defer func() { span.EndWithError(err) }() for _, aud := range audience { - if aud == clientID || aud == projectID { + if aud == introspectionClientID || aud == introspectionProjectID { userInfo := new(oidc.UserInfo) - err = o.setUserinfo(ctx, userInfo, subject, clientID, scope, []string{projectID}) // always + err = o.setUserinfo(ctx, userInfo, subject, introspectionClientID, scope, []string{introspectionProjectID}) if err != nil { return err } introspection.SetUserInfo(userInfo) introspection.Scope = scope - introspection.ClientID = clientID + introspection.ClientID = tokenClientID introspection.TokenType = oidc.BearerToken introspection.Expiration = oidc.FromTime(tokenExpiration) introspection.IssuedAt = oidc.FromTime(tokenCreation) From c0a9f23215b5ab5c7a1f7a08b6d04a65cc86d129 Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Fri, 4 Aug 2023 11:05:20 +0200 Subject: [PATCH 43/76] fix: project telemetry once for all instances (#6323) (cherry picked from commit d33a4fbb2f76e26e65b969b4f06bab13c73453c1) --- internal/notification/handlers/telemetry_pusher.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/notification/handlers/telemetry_pusher.go b/internal/notification/handlers/telemetry_pusher.go index 86f8420c7..837399b02 100644 --- a/internal/notification/handlers/telemetry_pusher.go +++ b/internal/notification/handlers/telemetry_pusher.go @@ -3,6 +3,7 @@ package handlers import ( "context" "fmt" + "math" "net/http" "time" @@ -57,6 +58,7 @@ func NewTelemetryPusher( handlerCfg.ProjectionName = TelemetryProjectionTable handlerCfg.Reducers = p.reducers() p.cfg = telemetryCfg + handlerCfg.ConcurrentInstances = math.MaxInt p.StatementHandler = crdb.NewStatementHandler(ctx, handlerCfg) p.commands = commands p.queries = queries From e000213817aaaa314f6c9e15931ce62ca21a494f Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 4 Aug 2023 11:35:36 +0200 Subject: [PATCH 44/76] fix: migrate external id of federated users (#6312) * feat: migrate external id * implement tests and some renaming * fix projection * cleanup * i18n * fix event type * handle migration for new services as well * typo (cherry picked from commit 45262e682972723412be631adaaa1dc50ce481e9) --- internal/api/idp/idp.go | 23 +++- .../api/ui/login/external_provider_handler.go | 55 +++++++- internal/auth/repository/auth_request.go | 2 +- .../eventsourcing/eventstore/auth_request.go | 7 +- internal/command/idp_intent_test.go | 89 +++++++++++++ internal/command/idp_model.go | 19 +++ internal/command/user_idp_link.go | 18 +++ internal/command/user_idp_link_model.go | 8 ++ internal/command/user_idp_link_test.go | 116 +++++++++++++++++ internal/idp/providers/azuread/azuread.go | 5 +- internal/idp/providers/azuread/session.go | 29 +++++ .../idp/providers/azuread/session_test.go | 117 +++++++++++++++++- internal/idp/session.go | 9 ++ internal/query/projection/idp_user_link.go | 25 ++++ .../query/projection/idp_user_link_test.go | 36 ++++++ internal/repository/user/eventstore.go | 1 + .../repository/user/human_external_idp.go | 39 ++++++ internal/static/i18n/bg.yaml | 2 + internal/static/i18n/de.yaml | 2 + internal/static/i18n/en.yaml | 2 + internal/static/i18n/es.yaml | 2 + internal/static/i18n/fr.yaml | 2 + internal/static/i18n/it.yaml | 2 + internal/static/i18n/ja.yaml | 2 + internal/static/i18n/mk.yaml | 2 + internal/static/i18n/pl.yaml | 2 + internal/static/i18n/pt.yaml | 2 + internal/static/i18n/zh.yaml | 2 + 28 files changed, 611 insertions(+), 9 deletions(-) create mode 100644 internal/idp/providers/azuread/session.go diff --git a/internal/api/idp/idp.go b/internal/api/idp/idp.go index 37a84f526..f9a3cd1f3 100644 --- a/internal/api/idp/idp.go +++ b/internal/api/idp/idp.go @@ -120,6 +120,11 @@ func (h *Handler) handleCallback(w http.ResponseWriter, r *http.Request) { userID, err := h.checkExternalUser(ctx, intent.IDPID, idpUser.GetID()) logging.WithFields("intent", intent.AggregateID).OnError(err).Error("could not check if idp user already exists") + if userID == "" { + userID, err = h.tryMigrateExternalUser(ctx, intent.IDPID, idpUser, idpSession) + logging.WithFields("intent", intent.AggregateID).OnError(err).Error("migration check failed") + } + token, err := h.commands.SucceedIDPIntent(ctx, intent, idpUser, idpSession, userID) if err != nil { redirectToFailureURLErr(w, r, intent, z_errs.ThrowInternal(err, "IDP-JdD3g", "Errors.Intent.TokenCreationFailed")) @@ -128,6 +133,22 @@ func (h *Handler) handleCallback(w http.ResponseWriter, r *http.Request) { redirectToSuccessURL(w, r, intent, token, userID) } +func (h *Handler) tryMigrateExternalUser(ctx context.Context, idpID string, idpUser idp.User, idpSession idp.Session) (userID string, err error) { + migration, ok := idpSession.(idp.SessionSupportsMigration) + if !ok { + return "", nil + } + previousID, err := migration.RetrievePreviousID() + if err != nil || previousID == "" { + return "", err + } + userID, err = h.checkExternalUser(ctx, idpID, previousID) + if err != nil { + return "", err + } + return userID, h.commands.MigrateUserIDP(ctx, userID, "", idpID, previousID, idpUser.GetID()) +} + func (h *Handler) parseCallbackRequest(r *http.Request) (*externalIDPCallbackData, error) { data := new(externalIDPCallbackData) err := h.parser.Parse(r, data) @@ -196,7 +217,7 @@ func (h *Handler) fetchIDPUser(ctx context.Context, identityProvider idp.Provide case *openid.Provider: session = &openid.Session{Provider: provider, Code: code} case *azuread.Provider: - session = &oauth.Session{Provider: provider.Provider, Code: code} + session = &azuread.Session{Session: &oauth.Session{Provider: provider.Provider, Code: code}} case *github.Provider: session = &oauth.Session{Provider: provider.Provider, Code: code} case *gitlab.Provider: diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index ee7696cc4..ceb2653a3 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -222,7 +222,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque l.externalAuthFailed(w, r, authReq, nil, nil, err) return } - session = &oauth.Session{Provider: provider.(*azuread.Provider).Provider, Code: data.Code} + session = &azuread.Session{Session: &oauth.Session{Provider: provider.(*azuread.Provider).Provider, Code: data.Code}} case domain.IDPTypeGitHub: provider, err = l.githubProvider(r.Context(), identityProvider) if err != nil { @@ -275,6 +275,46 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque l.handleExternalUserAuthenticated(w, r, authReq, identityProvider, session, user, l.renderNextStep) } +func (l *Login) tryMigrateExternalUserID(r *http.Request, session idp.Session, authReq *domain.AuthRequest, externalUser *domain.ExternalUser) (previousIDMatched bool, err error) { + migration, ok := session.(idp.SessionSupportsMigration) + if !ok { + return false, nil + } + previousID, err := migration.RetrievePreviousID() + if err != nil { + return false, err + } + return l.migrateExternalUserID(r, authReq, externalUser, previousID) +} + +func (l *Login) migrateExternalUserID(r *http.Request, authReq *domain.AuthRequest, externalUser *domain.ExternalUser, previousID string) (previousIDMatched bool, err error) { + if previousID == "" { + return false, nil + } + // save the currentID, so we're able to reset to it later on if the user is not found with the old ID as well + externalUserID := externalUser.ExternalUserID + externalUser.ExternalUserID = previousID + if err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, externalUser, domain.BrowserInfoFromRequest(r), true); err != nil { + // always reset to the mapped ID + externalUser.ExternalUserID = externalUserID + // but ignore the error if the user was just not found with the previousID + if errors.IsNotFound(err) { + return false, nil + } + return false, err + } + previousIDMatched = true + if err = l.authRepo.ResetLinkingUsers(r.Context(), authReq.ID, authReq.AgentID); err != nil { + return previousIDMatched, err + } + // read current auth request state (incl. authorized user) + authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) + if err != nil { + return previousIDMatched, err + } + return previousIDMatched, l.command.MigrateUserIDP(setContext(r.Context(), authReq.UserOrgID), authReq.UserID, authReq.UserOrgID, externalUser.IDPConfigID, previousID, externalUserID) +} + // handleExternalUserAuthenticated maps the IDP user, checks for a corresponding externalID func (l *Login) handleExternalUserAuthenticated( w http.ResponseWriter, @@ -287,11 +327,22 @@ func (l *Login) handleExternalUserAuthenticated( ) { externalUser := mapIDPUserToExternalUser(user, provider.ID) // check and fill in local linked user - externalErr := l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, externalUser, domain.BrowserInfoFromRequest(r)) + externalErr := l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, externalUser, domain.BrowserInfoFromRequest(r), false) if externalErr != nil && !errors.IsNotFound(externalErr) { l.renderError(w, r, authReq, externalErr) return } + if externalErr != nil && errors.IsNotFound(externalErr) { + previousIDMatched, err := l.tryMigrateExternalUserID(r, session, authReq, externalUser) + if err != nil { + l.renderError(w, r, authReq, err) + return + } + // if the old ID matched, ignore the not found error from the current ID + if previousIDMatched { + externalErr = nil + } + } var err error // read current auth request state (incl. authorized user) authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) diff --git a/internal/auth/repository/auth_request.go b/internal/auth/repository/auth_request.go index ffa70f9d6..8851b4f67 100644 --- a/internal/auth/repository/auth_request.go +++ b/internal/auth/repository/auth_request.go @@ -15,7 +15,7 @@ type AuthRequestRepository interface { DeleteAuthRequest(ctx context.Context, id string) error CheckLoginName(ctx context.Context, id, loginName, userAgentID string) error - CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser, info *domain.BrowserInfo) error + CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser, info *domain.BrowserInfo, migrationCheck bool) error SetExternalUserLogin(ctx context.Context, authReqID, userAgentID string, user *domain.ExternalUser) error SetLinkingUser(ctx context.Context, request *domain.AuthRequest, externalUser *domain.ExternalUser) error SelectUser(ctx context.Context, id, userID, userAgentID string) error diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index c37dd8a79..ea3377a50 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -238,7 +238,7 @@ func (repo *AuthRequestRepo) SelectExternalIDP(ctx context.Context, authReqID, i return repo.AuthRequests.UpdateAuthRequest(ctx, request) } -func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *domain.ExternalUser, info *domain.BrowserInfo) (err error) { +func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReqID, userAgentID string, externalUser *domain.ExternalUser, info *domain.BrowserInfo, migrationCheck bool) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() request, err := repo.getAuthRequest(ctx, authReqID, userAgentID) @@ -249,6 +249,11 @@ func (repo *AuthRequestRepo) CheckExternalUserLogin(ctx context.Context, authReq if errors.IsNotFound(err) { // clear potential user information (e.g. when username was entered but another external user was returned) request.SetUserInfo("", "", "", "", "", request.UserOrgID) + // in case the check was done with an ID, that was retrieved by a session that allows migration, + // we do not need to set the linking user and return early + if migrationCheck { + return err + } if err := repo.setLinkingUser(ctx, request, externalUser); err != nil { return err } diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index 08948a200..acbb1eebf 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -348,6 +348,95 @@ func TestCommands_AuthURLFromProvider(t *testing.T) { authURL: "auth?client_id=clientID&prompt=select_account&redirect_uri=url&response_type=code&state=state", }, }, + { + "migrated and push", + fields{ + secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), + eventstore: eventstoreExpect(t, + expectFilter( + eventFromEventPusherWithInstanceID( + "instance", + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid", "profile", "User.Read"}, + false, + rep_idp.Options{}, + )), + eventFromEventPusherWithInstanceID( + "instance", + instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid", "profile", "User.Read"}, + "tenant", + true, + rep_idp.Options{}, + )), + ), + expectFilter( + eventFromEventPusherWithInstanceID( + "instance", + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid", "profile", "User.Read"}, + false, + rep_idp.Options{}, + )), + eventFromEventPusherWithInstanceID( + "instance", + instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid", "profile", "User.Read"}, + "tenant", + true, + rep_idp.Options{}, + )), + ), + ), + }, + args{ + ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + idpID: "idp", + state: "state", + callbackURL: "url", + }, + res{ + authURL: "https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=url&response_type=code&scope=openid+profile+User.Read&state=state", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 0849c5bd0..417f98464 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -1653,6 +1653,14 @@ func (wm *IDPTypeWriteModel) Reduce() error { wm.reduceAdded(e.ID, domain.IDPTypeLDAP, e.Aggregate()) case *org.LDAPIDPAddedEvent: wm.reduceAdded(e.ID, domain.IDPTypeLDAP, e.Aggregate()) + case *instance.OIDCIDPMigratedAzureADEvent: + wm.reduceChanged(e.ID, domain.IDPTypeAzureAD) + case *org.OIDCIDPMigratedAzureADEvent: + wm.reduceChanged(e.ID, domain.IDPTypeAzureAD) + case *instance.OIDCIDPMigratedGoogleEvent: + wm.reduceChanged(e.ID, domain.IDPTypeGoogle) + case *org.OIDCIDPMigratedGoogleEvent: + wm.reduceChanged(e.ID, domain.IDPTypeGoogle) case *instance.IDPRemovedEvent: wm.reduceRemoved(e.ID) case *org.IDPRemovedEvent: @@ -1688,6 +1696,13 @@ func (wm *IDPTypeWriteModel) reduceAdded(id string, t domain.IDPType, agg events wm.InstanceID = agg.InstanceID } +func (wm *IDPTypeWriteModel) reduceChanged(id string, t domain.IDPType) { + if wm.ID != id { + return + } + wm.Type = t +} + func (wm *IDPTypeWriteModel) reduceRemoved(id string) { if wm.ID != id { return @@ -1713,6 +1728,8 @@ func (wm *IDPTypeWriteModel) Query() *eventstore.SearchQueryBuilder { instance.GitLabSelfHostedIDPAddedEventType, instance.GoogleIDPAddedEventType, instance.LDAPIDPAddedEventType, + instance.OIDCIDPMigratedAzureADEventType, + instance.OIDCIDPMigratedGoogleEventType, instance.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). @@ -1729,6 +1746,8 @@ func (wm *IDPTypeWriteModel) Query() *eventstore.SearchQueryBuilder { org.GitLabSelfHostedIDPAddedEventType, org.GoogleIDPAddedEventType, org.LDAPIDPAddedEventType, + org.OIDCIDPMigratedAzureADEventType, + org.OIDCIDPMigratedGoogleEventType, org.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). diff --git a/internal/command/user_idp_link.go b/internal/command/user_idp_link.go index 4c03f4643..46dd96b40 100644 --- a/internal/command/user_idp_link.go +++ b/internal/command/user_idp_link.go @@ -139,6 +139,24 @@ func (c *Commands) UserIDPLoginChecked(ctx context.Context, orgID, userID string return err } +func (c *Commands) MigrateUserIDP(ctx context.Context, userID, orgID, idpConfigID, previousID, newID string) (err error) { + if userID == "" { + return caos_errs.ThrowInvalidArgument(nil, "COMMAND-Sn3l1", "Errors.IDMissing") + } + + writeModel, err := c.userIDPLinkWriteModelByID(ctx, userID, idpConfigID, previousID, orgID) + if err != nil { + return err + } + if writeModel.State != domain.UserIDPLinkStateActive { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-KJH2o", "Errors.User.ExternalIDP.NotFound") + } + + userAgg := UserAggregateFromWriteModel(&writeModel.WriteModel) + _, err = c.eventstore.Push(ctx, user.NewUserIDPExternalIDMigratedEvent(ctx, userAgg, idpConfigID, previousID, newID)) + return err +} + func (c *Commands) userIDPLinkWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID, resourceOwner string) (writeModel *UserIDPLinkWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/user_idp_link_model.go b/internal/command/user_idp_link_model.go index 41ff5c6d5..edcd1ebd2 100644 --- a/internal/command/user_idp_link_model.go +++ b/internal/command/user_idp_link_model.go @@ -35,6 +35,11 @@ func (wm *UserIDPLinkWriteModel) AppendEvents(events ...eventstore.Event) { continue } wm.WriteModel.AppendEvents(e) + case *user.UserIDPExternalIDMigratedEvent: + if e.IDPConfigID != wm.IDPConfigID || e.PreviousID != wm.ExternalUserID { + continue + } + wm.WriteModel.AppendEvents(e) case *user.UserIDPLinkRemovedEvent: if e.IDPConfigID != wm.IDPConfigID || e.ExternalUserID != wm.ExternalUserID { continue @@ -59,6 +64,8 @@ func (wm *UserIDPLinkWriteModel) Reduce() error { wm.DisplayName = e.DisplayName wm.ExternalUserID = e.ExternalUserID wm.State = domain.UserIDPLinkStateActive + case *user.UserIDPExternalIDMigratedEvent: + wm.ExternalUserID = e.NewID case *user.UserIDPLinkRemovedEvent: wm.State = domain.UserIDPLinkStateRemoved case *user.UserIDPLinkCascadeRemovedEvent: @@ -77,6 +84,7 @@ func (wm *UserIDPLinkWriteModel) Query() *eventstore.SearchQueryBuilder { AggregateTypes(user.AggregateType). AggregateIDs(wm.AggregateID). EventTypes(user.UserIDPLinkAddedType, + user.UserIDPExternalIDMigratedType, user.UserIDPLinkRemovedType, user.UserIDPLinkCascadeRemovedType, user.UserRemovedType). diff --git a/internal/command/user_idp_link_test.go b/internal/command/user_idp_link_test.go index af6499785..207a8e9df 100644 --- a/internal/command/user_idp_link_test.go +++ b/internal/command/user_idp_link_test.go @@ -669,3 +669,119 @@ func TestCommandSide_ExternalLoginCheck(t *testing.T) { }) } } + +func TestCommandSide_MigrateUserIDP(t *testing.T) { + type fields struct { + eventstore func(t *testing.T) *eventstore.Eventstore + } + type args struct { + ctx context.Context + userID string + orgID string + idpConfigID string + previousID string + newID string + } + type res struct { + err error + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "userid missing, invalid argument error", + fields: fields{ + eventstore: expectEventstore(), + }, + args: args{ + ctx: context.Background(), + userID: "", + orgID: "org1", + idpConfigID: "idpConfig1", + previousID: "previousID", + newID: "newID", + }, + res: res{ + err: caos_errs.ThrowInvalidArgument(nil, "COMMAND-Sn3l1", "Errors.IDMissing"), + }, + }, + { + name: "idp link not active, precondition failed error", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpConfig1", + "displayName", + "externalUserID", + ), + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + orgID: "org1", + idpConfigID: "idpConfig1", + previousID: "previousID", + newID: "newID", + }, + res: res{ + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-KJH2o", "Errors.User.ExternalIDP.NotFound"), + }, + }, + { + name: "external login check, ok", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpConfig1", + "displayName", + "previousID", + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusher( + user.NewUserIDPExternalIDMigratedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpConfig1", + "previousID", + "newID", + ), + ), + }, + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + orgID: "org1", + idpConfigID: "idpConfig1", + previousID: "previousID", + newID: "newID", + }, + res: res{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore(t), + } + err := r.MigrateUserIDP(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.idpConfigID, tt.args.previousID, tt.args.newID) + assert.ErrorIs(t, err, tt.res.err) + }) + } +} diff --git a/internal/idp/providers/azuread/azuread.go b/internal/idp/providers/azuread/azuread.go index da1ce0ead..244f383e1 100644 --- a/internal/idp/providers/azuread/azuread.go +++ b/internal/idp/providers/azuread/azuread.go @@ -15,7 +15,8 @@ import ( const ( authURLTemplate string = "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize" tokenURLTemplate string = "https://login.microsoftonline.com/%s/oauth2/v2.0/token" - userinfoURL string = "https://graph.microsoft.com/v1.0/me" + userURL string = "https://graph.microsoft.com/v1.0/me" + userinfoEndpoint string = "https://graph.microsoft.com/oidc/userinfo" ScopeUserRead string = "User.Read" ) @@ -87,7 +88,7 @@ func New(name, clientID, clientSecret, redirectURI string, scopes []string, opts rp, err := oauth.New( config, name, - userinfoURL, + userURL, func() idp.User { return &User{isEmailVerified: provider.emailVerified} }, diff --git a/internal/idp/providers/azuread/session.go b/internal/idp/providers/azuread/session.go new file mode 100644 index 000000000..5bc7bb84c --- /dev/null +++ b/internal/idp/providers/azuread/session.go @@ -0,0 +1,29 @@ +package azuread + +import ( + "net/http" + + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" + + "github.com/zitadel/zitadel/internal/idp/providers/oauth" +) + +// Session extends the [oauth.Session] to extend it with the [idp.SessionSupportsMigration] functionality +type Session struct { + *oauth.Session +} + +// RetrievePreviousID implements the [idp.SessionSupportsMigration] interface by returning the `sub` from the userinfo endpoint +func (s *Session) RetrievePreviousID() (string, error) { + req, err := http.NewRequest("GET", userinfoEndpoint, nil) + if err != nil { + return "", err + } + req.Header.Set("authorization", s.Tokens.TokenType+" "+s.Tokens.AccessToken) + userinfo := new(oidc.UserInfo) + if err := httphelper.HttpRequest(s.Provider.HttpClient(), req, &userinfo); err != nil { + return "", err + } + return userinfo.Subject, nil +} diff --git a/internal/idp/providers/azuread/session_test.go b/internal/idp/providers/azuread/session_test.go index 5c6c45b4e..531d909b9 100644 --- a/internal/idp/providers/azuread/session_test.go +++ b/internal/idp/providers/azuread/session_test.go @@ -247,12 +247,12 @@ func TestSession_FetchUser(t *testing.T) { provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.options...) require.NoError(t, err) - session := &oauth.Session{ + session := &Session{Session: &oauth.Session{ AuthURL: tt.fields.authURL, Code: tt.fields.code, Tokens: tt.fields.tokens, Provider: provider.Provider, - } + }} user, err := session.FetchUser(context.Background()) if tt.want.err != nil && !tt.want.err(err) { @@ -294,3 +294,116 @@ func userinfo() *User { UserPrincipalName: "username", } } + +func TestSession_RetrievePreviousID(t *testing.T) { + type fields struct { + name string + clientID string + clientSecret string + redirectURI string + scopes []string + httpMock func() + tokens *oidc.Tokens[*oidc.IDTokenClaims] + } + type res struct { + id string + err bool + } + tests := []struct { + name string + fields fields + res res + }{ + { + name: "invalid token", + fields: fields{ + clientID: "clientID", + clientSecret: "clientSecret", + redirectURI: "redirectURI", + httpMock: func() { + gock.New("https://graph.microsoft.com"). + Get("/oidc/userinfo"). + Reply(401) + }, + tokens: &oidc.Tokens[*oidc.IDTokenClaims]{ + Token: &oauth2.Token{ + AccessToken: "accessToken", + TokenType: oidc.BearerToken, + }, + IDTokenClaims: oidc.NewIDTokenClaims( + "https://login.microsoftonline.com/consumers/oauth2/v2.0", + "sub", + []string{"clientID"}, + time.Now().Add(1*time.Hour), + time.Now().Add(-1*time.Second), + "nonce", + "", + nil, + "clientID", + 0, + ), + }, + }, + res: res{ + err: true, + }, + }, + { + name: "success", + fields: fields{ + clientID: "clientID", + clientSecret: "clientSecret", + redirectURI: "redirectURI", + httpMock: func() { + gock.New("https://graph.microsoft.com"). + Get("/oidc/userinfo"). + Reply(200). + JSON(`{"sub":"sub"}`) + }, + tokens: &oidc.Tokens[*oidc.IDTokenClaims]{ + Token: &oauth2.Token{ + AccessToken: "accessToken", + TokenType: oidc.BearerToken, + }, + IDTokenClaims: oidc.NewIDTokenClaims( + "https://login.microsoftonline.com/consumers/oauth2/v2.0", + "sub", + []string{"clientID"}, + time.Now().Add(1*time.Hour), + time.Now().Add(-1*time.Second), + "nonce", + "", + nil, + "clientID", + 0, + ), + }, + }, + res: res{ + id: "sub", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer gock.Off() + tt.fields.httpMock() + a := assert.New(t) + + provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes) + require.NoError(t, err) + session := &Session{Session: &oauth.Session{ + Tokens: tt.fields.tokens, + Provider: provider.Provider, + }} + + id, err := session.RetrievePreviousID() + if tt.res.err { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + a.Equal(tt.res.id, id) + }) + } +} diff --git a/internal/idp/session.go b/internal/idp/session.go index 8cc2dbfef..75f2e7a1e 100644 --- a/internal/idp/session.go +++ b/internal/idp/session.go @@ -9,3 +9,12 @@ type Session interface { GetAuthURL() string FetchUser(ctx context.Context) (User, error) } + +// SessionSupportsMigration is an optional extension to the Session interface. +// It can be implemented to support migrating users, were the initial external id has changed because of a migration of the Provider type. +// E.g. when a user was linked on a generic OIDC provider and this provider has now been migrated to an AzureAD provider. +// In this case OIDC used the `sub` claim and Azure now uses the id of the user endpoint, which differ. +// The RetrievePreviousID will return the `sub` claim again, so that the user can be matched and safely migrated to the new id. +type SessionSupportsMigration interface { + RetrievePreviousID() (previousID string, err error) +} diff --git a/internal/query/projection/idp_user_link.go b/internal/query/projection/idp_user_link.go index d4d3fb228..ed47ff1de 100644 --- a/internal/query/projection/idp_user_link.go +++ b/internal/query/projection/idp_user_link.go @@ -77,6 +77,10 @@ func (p *idpUserLinkProjection) reducers() []handler.AggregateReducer { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, }, + { + Event: user.UserIDPExternalIDMigratedType, + Reduce: p.reduceExternalIDMigrated, + }, }, }, { @@ -195,6 +199,27 @@ func (p *idpUserLinkProjection) reduceUserRemoved(event eventstore.Event) (*hand ), nil } +func (p *idpUserLinkProjection) reduceExternalIDMigrated(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*user.UserIDPExternalIDMigratedEvent](event) + if err != nil { + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-AS3th", "reduce.wrong.event.type %s", user.UserIDPExternalIDMigratedType) + } + + return crdb.NewUpdateStatement(e, + []handler.Column{ + handler.NewCol(IDPUserLinkChangeDateCol, e.CreationDate()), + handler.NewCol(IDPUserLinkSequenceCol, e.Sequence()), + handler.NewCol(IDPUserLinkExternalUserIDCol, e.NewID), + }, + []handler.Condition{ + handler.NewCond(IDPUserLinkIDPIDCol, e.IDPConfigID), + handler.NewCond(IDPUserLinkUserIDCol, e.Aggregate().ID), + handler.NewCond(IDPUserLinkExternalUserIDCol, e.PreviousID), + handler.NewCond(IDPUserLinkInstanceIDCol, e.Aggregate().InstanceID), + }, + ), nil +} + func (p *idpUserLinkProjection) reduceIDPConfigRemoved(event eventstore.Event) (*handler.Statement, error) { var idpID string diff --git a/internal/query/projection/idp_user_link_test.go b/internal/query/projection/idp_user_link_test.go index cc6548a98..a343fe0b6 100644 --- a/internal/query/projection/idp_user_link_test.go +++ b/internal/query/projection/idp_user_link_test.go @@ -207,6 +207,42 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { }, }, }, + { + name: "reduceExternalIDMigrated", + args: args{ + event: getEvent(testEvent( + repository.EventType(user.UserIDPExternalIDMigratedType), + user.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "previousId": "previous-id", + "newId": "new-id" +}`), + ), eventstore.GenericEventMapper[user.UserIDPExternalIDMigratedEvent]), + }, + reduce: (&idpUserLinkProjection{}).reduceExternalIDMigrated, + want: wantReduce{ + aggregateType: user.AggregateType, + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_user_links3 SET (change_date, sequence, external_user_id) = ($1, $2, $3) WHERE (idp_id = $4) AND (user_id = $5) AND (external_user_id = $6) AND (instance_id = $7)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "new-id", + "idp-config-id", + "agg-id", + "previous-id", + "instance-id", + }, + }, + }, + }, + }, + }, { name: "org IDPConfigRemovedEvent", args: args{ diff --git a/internal/repository/user/eventstore.go b/internal/repository/user/eventstore.go index 1505c31cf..814404ca5 100644 --- a/internal/repository/user/eventstore.go +++ b/internal/repository/user/eventstore.go @@ -67,6 +67,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, UserIDPLinkRemovedType, UserIDPLinkRemovedEventMapper). RegisterFilterEventMapper(AggregateType, UserIDPLinkCascadeRemovedType, UserIDPLinkCascadeRemovedEventMapper). RegisterFilterEventMapper(AggregateType, UserIDPLoginCheckSucceededType, UserIDPCheckSucceededEventMapper). + RegisterFilterEventMapper(AggregateType, UserIDPExternalIDMigratedType, eventstore.GenericEventMapper[UserIDPExternalIDMigratedEvent]). RegisterFilterEventMapper(AggregateType, HumanEmailChangedType, HumanEmailChangedEventMapper). RegisterFilterEventMapper(AggregateType, HumanEmailVerifiedType, HumanEmailVerifiedEventMapper). RegisterFilterEventMapper(AggregateType, HumanEmailVerificationFailedType, HumanEmailVerificationFailedEventMapper). diff --git a/internal/repository/user/human_external_idp.go b/internal/repository/user/human_external_idp.go index 6a279951a..e5a62e00b 100644 --- a/internal/repository/user/human_external_idp.go +++ b/internal/repository/user/human_external_idp.go @@ -18,6 +18,7 @@ const ( UserIDPLinkAddedType = UserIDPLinkEventPrefix + "added" UserIDPLinkRemovedType = UserIDPLinkEventPrefix + "removed" UserIDPLinkCascadeRemovedType = UserIDPLinkEventPrefix + "cascade.removed" + UserIDPExternalIDMigratedType = UserIDPLinkEventPrefix + "id.migrated" UserIDPLoginCheckSucceededType = idpLoginEventPrefix + "check.succeeded" ) @@ -212,3 +213,41 @@ func UserIDPCheckSucceededEventMapper(event *repository.Event) (eventstore.Event return e, nil } + +type UserIDPExternalIDMigratedEvent struct { + eventstore.BaseEvent `json:"-"` + IDPConfigID string `json:"idpConfigId"` + PreviousID string `json:"previousId"` + NewID string `json:"newId"` +} + +func (e *UserIDPExternalIDMigratedEvent) Data() interface{} { + return e +} + +func (e *UserIDPExternalIDMigratedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func (e *UserIDPExternalIDMigratedEvent) SetBaseEvent(event *eventstore.BaseEvent) { + e.BaseEvent = *event +} + +func NewUserIDPExternalIDMigratedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + idpConfigID, + previousID, + newID string, +) *UserIDPExternalIDMigratedEvent { + return &UserIDPExternalIDMigratedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + UserIDPExternalIDMigratedType, + ), + IDPConfigID: idpConfigID, + PreviousID: previousID, + NewID: newID, + } +} diff --git a/internal/static/i18n/bg.yaml b/internal/static/i18n/bg.yaml index 33dd828a7..9f0d302cd 100644 --- a/internal/static/i18n/bg.yaml +++ b/internal/static/i18n/bg.yaml @@ -607,6 +607,8 @@ EventTypes: removed: Външният IDP е премахнат cascade: removed: Външната IDP каскада е премахната + id: + migrated: Външният потребителски идентификатор на IDP беше мигриран phone: changed: Телефонният номер е променен verified: Телефонният номер е потвърден diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index b485f65c6..0607b9cf2 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -595,6 +595,8 @@ EventTypes: removed: Externer IDP wurde gelöscht cascade: removed: Externer IDP wurde kaskadiert gelöscht + id: + migrated: Externe UserID des IDP wurde migriert phone: changed: Telefonnummer geändert verified: Telefonnummer verifiziert diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 93a529045..b9f92456d 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -595,6 +595,8 @@ EventTypes: removed: External IDP removed cascade: removed: External IDP cascade removed + id: + migrated: External UserID of IDP was migrated phone: changed: Phone number changed verified: Phone number verified diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index 5dc4a3ab4..e45a07591 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -595,6 +595,8 @@ EventTypes: removed: IDP externo eliminado cascade: removed: IDP externo eliminado en cascada + id: + migrated: Se migró el ID de usuario externo del IDP phone: changed: Número de teléfono modificado verified: Número de teléfono verificado diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index b80a9ed33..150d9b030 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -593,6 +593,8 @@ EventTypes: removed: Externer IDP supprimé cascade: removed: Externer IDP cascade supprimé + îd: + migrated: L'ID utilisateur externe de l'IDP a été migré phone: changed: Le numéro de téléphone a changé verified: Numéro de téléphone vérifié diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index e1bd873bb..fc50f082c 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -593,6 +593,8 @@ EventTypes: removed: IDP esterno rimosso cascade: removed: Cascata IDP rimossa + id: + migrated: L'ID utente esterno dell'IDP è stato migrato phone: changed: Numero di telefono cambiato verified: Numero di telefono verificato diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index eadf7ad5a..bbf565e07 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -580,6 +580,8 @@ EventTypes: removed: 外部IDPの削除 cascade: removed: 外部IDPカスケードの削除 + id: + migrated: IDP の外部ユーザー ID が移行されました phone: changed: 電話番号の変更 verified: 電話番号の検証 diff --git a/internal/static/i18n/mk.yaml b/internal/static/i18n/mk.yaml index c3fcfdf69..78d059f1f 100644 --- a/internal/static/i18n/mk.yaml +++ b/internal/static/i18n/mk.yaml @@ -591,6 +591,8 @@ EventTypes: removed: Отстранет надворешен IDP cascade: removed: Отстранета каскадата на надворешни IDP + id: + migrated: Надворешниот кориснички ID на IDP е мигриран phone: changed: Променет број на телефон verified: Верифициран број на телефон diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index aa84c8ca5..fb7b80114 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -595,6 +595,8 @@ EventTypes: removed: Usunięto zewnętrzne IDP cascade: removed: Usunięto kaskadę zewnętrznego IDP + id: + migrated: Identyfikator użytkownika zewnętrznego dostawcy tożsamości został przeniesiony phone: changed: Numer telefonu zmieniony verified: Numer telefonu zweryfikowany diff --git a/internal/static/i18n/pt.yaml b/internal/static/i18n/pt.yaml index 4f3c5af85..932ed6654 100644 --- a/internal/static/i18n/pt.yaml +++ b/internal/static/i18n/pt.yaml @@ -586,6 +586,8 @@ EventTypes: removed: IDP externo removido cascade: removed: Cascade de IDP externo removido + id: + migrated: O ID de usuário externo do IDP foi migrado phone: changed: Número de telefone alterado verified: Número de telefone verificado diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index fb068a233..10f7072d3 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -589,6 +589,8 @@ EventTypes: removed: 移除了外部 IDP cascade: removed: 移除了外部 IDP + id: + migrated: IDP 的外部用户 ID 已迁移 phone: changed: 修改手机号码 verified: 已验证手机号码 From 27513b7bfd3031657d04c8bb9f4551c102e66047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Fri, 4 Aug 2023 18:16:27 +0300 Subject: [PATCH 45/76] fix: trigger session by id in verifySessionToken (#6325) (cherry picked from commit 11b5a735518db7596b49f022ade62b41ce0218cd) --- .../authz/repository/eventsourcing/eventstore/token_verifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index 1e2153863..5563db37d 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -147,7 +147,7 @@ func (repo *TokenVerifierRepo) verifySessionToken(ctx context.Context, sessionID ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - session, err := repo.Query.SessionByID(ctx, false, sessionID, token) + session, err := repo.Query.SessionByID(ctx, true, sessionID, token) if err != nil { return "", "", "", err } From 61742139e091a39075cd7e9c80a1a19105f3c615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Fri, 4 Aug 2023 19:17:16 +0300 Subject: [PATCH 46/76] fix: always update the timestamp in trigger (#6326) * always reset timestamp * re-enable test (cherry picked from commit 3c7b603650531e9cf69b70c93b38cd163cf12f52) --- .../api/grpc/management/user_integration_test.go | 12 +++++++++--- internal/eventstore/handler/handler_projection.go | 6 ++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/api/grpc/management/user_integration_test.go b/internal/api/grpc/management/user_integration_test.go index c1682a5cc..ab1e149bd 100644 --- a/internal/api/grpc/management/user_integration_test.go +++ b/internal/api/grpc/management/user_integration_test.go @@ -5,11 +5,20 @@ package management_test import ( "context" "os" + "strconv" + "strings" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/pkg/grpc/management" + "github.com/zitadel/zitadel/pkg/grpc/user" ) var ( @@ -38,8 +47,6 @@ func TestMain(m *testing.M) { // This test Imports a user and directly tries to Get it, 100 times in a loop. // When the bug still existed, some (between 1 to 7 out of 100) // Get calls would return a Not Found error. - -/* Test disabled because it breaks the pipeline. func TestImport_and_Get(t *testing.T) { const N = 100 var misses int @@ -84,4 +91,3 @@ func TestImport_and_Get(t *testing.T) { } assert.Zerof(t, misses, "Not Found errors %d out of %d", misses, N) } -*/ diff --git a/internal/eventstore/handler/handler_projection.go b/internal/eventstore/handler/handler_projection.go index baab2d3a6..dcd24b3d7 100644 --- a/internal/eventstore/handler/handler_projection.go +++ b/internal/eventstore/handler/handler_projection.go @@ -140,8 +140,11 @@ func (h *ProjectionHandler) Trigger(ctx context.Context, instances ...string) co // by calling FetchEvents and Process until the amount of events is smaller than the BulkLimit. // If a bulk action was executed, the call timestamp in context will be reset for subsequent queries. // The returned context is never nil. It is either the original context or an updated context. -func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) (context.Context, error) { +func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) (outCtx context.Context, err error) { instances = triggerInstances(ctx, instances) + defer func() { + outCtx = call.ResetTimestamp(ctx) + }() for { events, hasLimitExceeded, err := h.FetchEvents(ctx, instances...) if err != nil { @@ -151,7 +154,6 @@ func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) return ctx, nil } _, err = h.Process(ctx, events...) - ctx = call.ResetTimestamp(ctx) if err != nil { return ctx, err } From 5828dd493d5840cab5b460994d13505d98bc75e7 Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Fri, 4 Aug 2023 20:12:44 +0200 Subject: [PATCH 47/76] fix: add texts after template reset (#6237) * fix: add texts after template reset * fix unit tests (cherry picked from commit d937ee3dda0620cf764d7404d23105cd573411c9) --- internal/command/custom_login_text.go | 444 +++++++++--------- .../command/org_custom_login_text_test.go | 6 + 2 files changed, 228 insertions(+), 222 deletions(-) diff --git a/internal/command/custom_login_text.go b/internal/command/custom_login_text.go index 21147c616..bc530f00c 100644 --- a/internal/command/custom_login_text.go +++ b/internal/command/custom_login_text.go @@ -52,35 +52,35 @@ func (c *Commands) createAllLoginTextEvents(ctx context.Context, agg *eventstore func (c *Commands) createSelectLoginTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountTitle, existingText.SelectAccountTitle, text.SelectAccount.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountTitle, existingText.SelectAccountTitle, text.SelectAccount.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountDescription, existingText.SelectAccountDescription, text.SelectAccount.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountDescription, existingText.SelectAccountDescription, text.SelectAccount.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountTitleLinkingProcess, existingText.SelectAccountTitleLinkingProcess, text.SelectAccount.TitleLinking, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountTitleLinkingProcess, existingText.SelectAccountTitleLinkingProcess, text.SelectAccount.TitleLinking, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountDescriptionLinkingProcess, existingText.SelectAccountDescriptionLinkingProcess, text.SelectAccount.DescriptionLinking, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountDescriptionLinkingProcess, existingText.SelectAccountDescriptionLinkingProcess, text.SelectAccount.DescriptionLinking, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountOtherUser, existingText.SelectAccountOtherUser, text.SelectAccount.OtherUser, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountOtherUser, existingText.SelectAccountOtherUser, text.SelectAccount.OtherUser, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountSessionStateActive, existingText.SelectAccountSessionStateActive, text.SelectAccount.SessionState0, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountSessionStateActive, existingText.SelectAccountSessionStateActive, text.SelectAccount.SessionState0, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountSessionStateInactive, existingText.SelectAccountSessionStateInactive, text.SelectAccount.SessionState1, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountSessionStateInactive, existingText.SelectAccountSessionStateInactive, text.SelectAccount.SessionState1, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, existingText.SelectAccountUserMustBeMemberOfOrg, text.SelectAccount.MustBeMemberOfOrg, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, existingText.SelectAccountUserMustBeMemberOfOrg, text.SelectAccount.MustBeMemberOfOrg, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -89,47 +89,47 @@ func (c *Commands) createSelectLoginTextEvents(ctx context.Context, agg *eventst func (c *Commands) createLoginTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginTitle, existingText.LoginTitle, text.Login.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginTitle, existingText.LoginTitle, text.Login.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginDescription, existingText.LoginDescription, text.Login.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginDescription, existingText.LoginDescription, text.Login.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginTitleLinkingProcess, existingText.LoginTitleLinkingProcess, text.Login.TitleLinking, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginTitleLinkingProcess, existingText.LoginTitleLinkingProcess, text.Login.TitleLinking, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginDescriptionLinkingProcess, existingText.LoginDescriptionLinkingProcess, text.Login.DescriptionLinking, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginDescriptionLinkingProcess, existingText.LoginDescriptionLinkingProcess, text.Login.DescriptionLinking, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginNameLabel, existingText.LoginNameLabel, text.Login.LoginNameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginNameLabel, existingText.LoginNameLabel, text.Login.LoginNameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginUsernamePlaceHolder, existingText.LoginUsernamePlaceholder, text.Login.UsernamePlaceholder, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginUsernamePlaceHolder, existingText.LoginUsernamePlaceholder, text.Login.UsernamePlaceholder, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginLoginnamePlaceHolder, existingText.LoginLoginnamePlaceholder, text.Login.LoginnamePlaceholder, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginLoginnamePlaceHolder, existingText.LoginLoginnamePlaceholder, text.Login.LoginnamePlaceholder, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginRegisterButtonText, existingText.LoginRegisterButtonText, text.Login.RegisterButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginRegisterButtonText, existingText.LoginRegisterButtonText, text.Login.RegisterButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginNextButtonText, existingText.LoginNextButtonText, text.Login.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginNextButtonText, existingText.LoginNextButtonText, text.Login.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginExternalUserDescription, existingText.LoginExternalUserDescription, text.Login.ExternalUserDescription, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginExternalUserDescription, existingText.LoginExternalUserDescription, text.Login.ExternalUserDescription, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginUserMustBeMemberOfOrg, existingText.LoginUserMustBeMemberOfOrg, text.Login.MustBeMemberOfOrg, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLoginUserMustBeMemberOfOrg, existingText.LoginUserMustBeMemberOfOrg, text.Login.MustBeMemberOfOrg, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -138,51 +138,51 @@ func (c *Commands) createLoginTextEvents(ctx context.Context, agg *eventstore.Ag func (c *Commands) createPasswordTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordTitle, existingText.PasswordTitle, text.Password.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordTitle, existingText.PasswordTitle, text.Password.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordDescription, existingText.PasswordDescription, text.Password.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordDescription, existingText.PasswordDescription, text.Password.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordLabel, existingText.PasswordLabel, text.Password.PasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordLabel, existingText.PasswordLabel, text.Password.PasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetLinkText, existingText.PasswordResetLinkText, text.Password.ResetLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetLinkText, existingText.PasswordResetLinkText, text.Password.ResetLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordBackButtonText, existingText.PasswordBackButtonText, text.Password.BackButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordBackButtonText, existingText.PasswordBackButtonText, text.Password.BackButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordNextButtonText, existingText.PasswordNextButtonText, text.Password.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordNextButtonText, existingText.PasswordNextButtonText, text.Password.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordMinLength, existingText.PasswordMinLength, text.Password.MinLength, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordMinLength, existingText.PasswordMinLength, text.Password.MinLength, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasUppercase, existingText.PasswordHasUppercase, text.Password.HasUppercase, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasUppercase, existingText.PasswordHasUppercase, text.Password.HasUppercase, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasLowercase, existingText.PasswordHasLowercase, text.Password.HasLowercase, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasLowercase, existingText.PasswordHasLowercase, text.Password.HasLowercase, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasNumber, existingText.PasswordHasNumber, text.Password.HasNumber, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasNumber, existingText.PasswordHasNumber, text.Password.HasNumber, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasSymbol, existingText.PasswordHasSymbol, text.Password.HasSymbol, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordHasSymbol, existingText.PasswordHasSymbol, text.Password.HasSymbol, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordConfirmation, existingText.PasswordConfirmation, text.Password.Confirmation, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordConfirmation, existingText.PasswordConfirmation, text.Password.Confirmation, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -191,23 +191,23 @@ func (c *Commands) createPasswordTextEvents(ctx context.Context, agg *eventstore func (c *Commands) createUsernameChangeTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeTitle, existingText.UsernameChangeTitle, text.UsernameChange.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeTitle, existingText.UsernameChangeTitle, text.UsernameChange.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDescription, existingText.UsernameChangeDescription, text.UsernameChange.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDescription, existingText.UsernameChangeDescription, text.UsernameChange.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeUsernameLabel, existingText.UsernameChangeUsernameLabel, text.UsernameChange.UsernameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeUsernameLabel, existingText.UsernameChangeUsernameLabel, text.UsernameChange.UsernameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeCancelButtonText, existingText.UsernameChangeCancelButtonText, text.UsernameChange.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeCancelButtonText, existingText.UsernameChangeCancelButtonText, text.UsernameChange.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeNextButtonText, existingText.UsernameChangeNextButtonText, text.UsernameChange.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeNextButtonText, existingText.UsernameChangeNextButtonText, text.UsernameChange.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -216,15 +216,15 @@ func (c *Commands) createUsernameChangeTextEvents(ctx context.Context, agg *even func (c *Commands) createUsernameChangeDoneTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneTitle, existingText.UsernameChangeDoneTitle, text.UsernameChangeDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneTitle, existingText.UsernameChangeDoneTitle, text.UsernameChangeDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneDescription, existingText.UsernameChangeDoneDescription, text.UsernameChangeDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneDescription, existingText.UsernameChangeDoneDescription, text.UsernameChangeDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneNextButtonText, existingText.UsernameChangeDoneNextButtonText, text.UsernameChangeDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyUsernameChangeDoneNextButtonText, existingText.UsernameChangeDoneNextButtonText, text.UsernameChangeDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -233,31 +233,31 @@ func (c *Commands) createUsernameChangeDoneTextEvents(ctx context.Context, agg * func (c *Commands) createPasswordInitTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordTitle, existingText.InitPasswordTitle, text.InitPassword.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordTitle, existingText.InitPasswordTitle, text.InitPassword.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDescription, existingText.InitPasswordDescription, text.InitPassword.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDescription, existingText.InitPasswordDescription, text.InitPassword.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordCodeLabel, existingText.InitPasswordCodeLabel, text.InitPassword.CodeLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordCodeLabel, existingText.InitPasswordCodeLabel, text.InitPassword.CodeLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNewPasswordLabel, existingText.InitPasswordNewPasswordLabel, text.InitPassword.NewPasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNewPasswordLabel, existingText.InitPasswordNewPasswordLabel, text.InitPassword.NewPasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, existingText.InitPasswordNewPasswordConfirmLabel, text.InitPassword.NewPasswordConfirmLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, existingText.InitPasswordNewPasswordConfirmLabel, text.InitPassword.NewPasswordConfirmLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNextButtonText, existingText.InitPasswordNextButtonText, text.InitPassword.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordNextButtonText, existingText.InitPasswordNextButtonText, text.InitPassword.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordResendButtonText, existingText.InitPasswordResendButtonText, text.InitPassword.ResendButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordResendButtonText, existingText.InitPasswordResendButtonText, text.InitPassword.ResendButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -266,19 +266,19 @@ func (c *Commands) createPasswordInitTextEvents(ctx context.Context, agg *events func (c *Commands) createPasswordInitDoneTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneTitle, existingText.InitPasswordDoneTitle, text.InitPasswordDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneTitle, existingText.InitPasswordDoneTitle, text.InitPasswordDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneDescription, existingText.InitPasswordDoneDescription, text.InitPasswordDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneDescription, existingText.InitPasswordDoneDescription, text.InitPasswordDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneNextButtonText, existingText.InitPasswordDoneNextButtonText, text.InitPasswordDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneNextButtonText, existingText.InitPasswordDoneNextButtonText, text.InitPasswordDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneCancelButtonText, existingText.InitPasswordDoneCancelButtonText, text.InitPasswordDone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitPasswordDoneCancelButtonText, existingText.InitPasswordDoneCancelButtonText, text.InitPasswordDone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -287,23 +287,23 @@ func (c *Commands) createPasswordInitDoneTextEvents(ctx context.Context, agg *ev func (c *Commands) createEmailVerificationTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationTitle, existingText.EmailVerificationTitle, text.EmailVerification.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationTitle, existingText.EmailVerificationTitle, text.EmailVerification.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDescription, existingText.EmailVerificationDescription, text.EmailVerification.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDescription, existingText.EmailVerificationDescription, text.EmailVerification.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationCodeLabel, existingText.EmailVerificationCodeLabel, text.EmailVerification.CodeLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationCodeLabel, existingText.EmailVerificationCodeLabel, text.EmailVerification.CodeLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationNextButtonText, existingText.EmailVerificationNextButtonText, text.EmailVerification.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationNextButtonText, existingText.EmailVerificationNextButtonText, text.EmailVerification.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationResendButtonText, existingText.EmailVerificationResendButtonText, text.EmailVerification.ResendButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationResendButtonText, existingText.EmailVerificationResendButtonText, text.EmailVerification.ResendButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -312,23 +312,23 @@ func (c *Commands) createEmailVerificationTextEvents(ctx context.Context, agg *e func (c *Commands) createEmailVerificationDoneTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneTitle, existingText.EmailVerificationDoneTitle, text.EmailVerificationDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneTitle, existingText.EmailVerificationDoneTitle, text.EmailVerificationDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneDescription, existingText.EmailVerificationDoneDescription, text.EmailVerificationDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneDescription, existingText.EmailVerificationDoneDescription, text.EmailVerificationDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneNextButtonText, existingText.EmailVerificationDoneNextButtonText, text.EmailVerificationDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneNextButtonText, existingText.EmailVerificationDoneNextButtonText, text.EmailVerificationDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneCancelButtonText, existingText.EmailVerificationDoneCancelButtonText, text.EmailVerificationDone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneCancelButtonText, existingText.EmailVerificationDoneCancelButtonText, text.EmailVerificationDone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneLoginButtonText, existingText.EmailVerificationDoneLoginButtonText, text.EmailVerificationDone.LoginButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyEmailVerificationDoneLoginButtonText, existingText.EmailVerificationDoneLoginButtonText, text.EmailVerificationDone.LoginButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -337,31 +337,31 @@ func (c *Commands) createEmailVerificationDoneTextEvents(ctx context.Context, ag func (c *Commands) createInitUserEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserTitle, existingText.InitializeTitle, text.InitUser.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserTitle, existingText.InitializeTitle, text.InitUser.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserDescription, existingText.InitializeDescription, text.InitUser.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserDescription, existingText.InitializeDescription, text.InitUser.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserCodeLabel, existingText.InitializeCodeLabel, text.InitUser.CodeLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserCodeLabel, existingText.InitializeCodeLabel, text.InitUser.CodeLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNewPasswordLabel, existingText.InitializeNewPassword, text.InitUser.NewPasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNewPasswordLabel, existingText.InitializeNewPassword, text.InitUser.NewPasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, existingText.InitializeNewPasswordConfirm, text.InitUser.NewPasswordConfirmLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, existingText.InitializeNewPasswordConfirm, text.InitUser.NewPasswordConfirmLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserResendButtonText, existingText.InitializeResendButtonText, text.InitUser.ResendButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserResendButtonText, existingText.InitializeResendButtonText, text.InitUser.ResendButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNextButtonText, existingText.InitializeNextButtonText, text.InitUser.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitializeUserNextButtonText, existingText.InitializeNextButtonText, text.InitUser.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -370,19 +370,19 @@ func (c *Commands) createInitUserEvents(ctx context.Context, agg *eventstore.Agg func (c *Commands) createInitUserDoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneTitle, existingText.InitializeDoneTitle, text.InitUserDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneTitle, existingText.InitializeDoneTitle, text.InitUserDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneDescription, existingText.InitializeDoneDescription, text.InitUserDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneDescription, existingText.InitializeDoneDescription, text.InitUserDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneCancelButtonText, existingText.InitializeDoneAbortButtonText, text.InitUserDone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneCancelButtonText, existingText.InitializeDoneAbortButtonText, text.InitUserDone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneNextButtonText, existingText.InitializeDoneNextButtonText, text.InitUserDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitUserDoneNextButtonText, existingText.InitializeDoneNextButtonText, text.InitUserDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -391,27 +391,27 @@ func (c *Commands) createInitUserDoneEvents(ctx context.Context, agg *eventstore func (c *Commands) createInitMFAPromptEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptTitle, existingText.InitMFAPromptTitle, text.InitMFAPrompt.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptTitle, existingText.InitMFAPromptTitle, text.InitMFAPrompt.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptDescription, existingText.InitMFAPromptDescription, text.InitMFAPrompt.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptDescription, existingText.InitMFAPromptDescription, text.InitMFAPrompt.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptOTPOption, existingText.InitMFAPromptOTPOption, text.InitMFAPrompt.Provider0, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptOTPOption, existingText.InitMFAPromptOTPOption, text.InitMFAPrompt.Provider0, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptU2FOption, existingText.InitMFAPromptU2FOption, text.InitMFAPrompt.Provider1, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptU2FOption, existingText.InitMFAPromptU2FOption, text.InitMFAPrompt.Provider1, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptSkipButtonText, existingText.InitMFAPromptSkipButtonText, text.InitMFAPrompt.SkipButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptSkipButtonText, existingText.InitMFAPromptSkipButtonText, text.InitMFAPrompt.SkipButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptNextButtonText, existingText.InitMFAPromptNextButtonText, text.InitMFAPrompt.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAPromptNextButtonText, existingText.InitMFAPromptNextButtonText, text.InitMFAPrompt.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -420,31 +420,31 @@ func (c *Commands) createInitMFAPromptEvents(ctx context.Context, agg *eventstor func (c *Commands) createInitMFAOTPEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPTitle, existingText.InitMFAOTPTitle, text.InitMFAOTP.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPTitle, existingText.InitMFAOTPTitle, text.InitMFAOTP.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPDescription, existingText.InitMFAOTPDescription, text.InitMFAOTP.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPDescription, existingText.InitMFAOTPDescription, text.InitMFAOTP.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPDescriptionOTP, existingText.InitMFAOTPDescriptionOTP, text.InitMFAOTP.OTPDescription, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPDescriptionOTP, existingText.InitMFAOTPDescriptionOTP, text.InitMFAOTP.OTPDescription, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPSecretLabel, existingText.InitMFAOTPSecretLabel, text.InitMFAOTP.SecretLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPSecretLabel, existingText.InitMFAOTPSecretLabel, text.InitMFAOTP.SecretLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPCodeLabel, existingText.InitMFAOTPCodeLabel, text.InitMFAOTP.CodeLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPCodeLabel, existingText.InitMFAOTPCodeLabel, text.InitMFAOTP.CodeLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPNextButtonText, existingText.InitMFAOTPNextButtonText, text.InitMFAOTP.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPNextButtonText, existingText.InitMFAOTPNextButtonText, text.InitMFAOTP.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPCancelButtonText, existingText.InitMFAOTPCancelButtonText, text.InitMFAOTP.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAOTPCancelButtonText, existingText.InitMFAOTPCancelButtonText, text.InitMFAOTP.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -453,27 +453,27 @@ func (c *Commands) createInitMFAOTPEvents(ctx context.Context, agg *eventstore.A func (c *Commands) createInitMFAU2FEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FTitle, existingText.InitMFAU2FTitle, text.InitMFAU2F.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FTitle, existingText.InitMFAU2FTitle, text.InitMFAU2F.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FDescription, existingText.InitMFAU2FDescription, text.InitMFAU2F.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FDescription, existingText.InitMFAU2FDescription, text.InitMFAU2F.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FTokenNameLabel, existingText.InitMFAU2FTokenNameLabel, text.InitMFAU2F.TokenNameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FTokenNameLabel, existingText.InitMFAU2FTokenNameLabel, text.InitMFAU2F.TokenNameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, existingText.InitMFAU2FRegisterTokenButtonText, text.InitMFAU2F.RegisterTokenButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, existingText.InitMFAU2FRegisterTokenButtonText, text.InitMFAU2F.RegisterTokenButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FNotSupported, existingText.InitMFAU2FNotSupported, text.InitMFAU2F.NotSupported, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FNotSupported, existingText.InitMFAU2FNotSupported, text.InitMFAU2F.NotSupported, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FErrorRetry, existingText.InitMFAU2FErrorRetry, text.InitMFAU2F.ErrorRetry, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFAU2FErrorRetry, existingText.InitMFAU2FErrorRetry, text.InitMFAU2F.ErrorRetry, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -482,19 +482,19 @@ func (c *Commands) createInitMFAU2FEvents(ctx context.Context, agg *eventstore.A func (c *Commands) createInitMFADoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneTitle, existingText.InitMFADoneTitle, text.InitMFADone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneTitle, existingText.InitMFADoneTitle, text.InitMFADone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneDescription, existingText.InitMFADoneDescription, text.InitMFADone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneDescription, existingText.InitMFADoneDescription, text.InitMFADone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneCancelButtonText, existingText.InitMFADoneAbortButtonText, text.InitMFADone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneCancelButtonText, existingText.InitMFADoneAbortButtonText, text.InitMFADone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneNextButtonText, existingText.InitMFADoneNextButtonText, text.InitMFADone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyInitMFADoneNextButtonText, existingText.InitMFADoneNextButtonText, text.InitMFADone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -503,15 +503,15 @@ func (c *Commands) createInitMFADoneEvents(ctx context.Context, agg *eventstore. func (c *Commands) crateMFAProviderEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersChooseOther, existingText.MFAProvidersChooseOther, text.MFAProvider.ChooseOther, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersChooseOther, existingText.MFAProvidersChooseOther, text.MFAProvider.ChooseOther, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersOTP, existingText.MFAProvidersOTP, text.MFAProvider.Provider0, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersOTP, existingText.MFAProvidersOTP, text.MFAProvider.Provider0, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersU2F, existingText.MFAProvidersU2F, text.MFAProvider.Provider1, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyMFAProvidersU2F, existingText.MFAProvidersU2F, text.MFAProvider.Provider1, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -520,19 +520,19 @@ func (c *Commands) crateMFAProviderEvents(ctx context.Context, agg *eventstore.A func (c *Commands) createVerifyMFAOTPEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPTitle, existingText.VerifyMFAOTPTitle, text.VerifyMFAOTP.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPTitle, existingText.VerifyMFAOTPTitle, text.VerifyMFAOTP.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPDescription, existingText.VerifyMFAOTPDescription, text.VerifyMFAOTP.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPDescription, existingText.VerifyMFAOTPDescription, text.VerifyMFAOTP.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPCodeLabel, existingText.VerifyMFAOTPCodeLabel, text.VerifyMFAOTP.CodeLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPCodeLabel, existingText.VerifyMFAOTPCodeLabel, text.VerifyMFAOTP.CodeLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPNextButtonText, existingText.VerifyMFAOTPNextButtonText, text.VerifyMFAOTP.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAOTPNextButtonText, existingText.VerifyMFAOTPNextButtonText, text.VerifyMFAOTP.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -541,23 +541,23 @@ func (c *Commands) createVerifyMFAOTPEvents(ctx context.Context, agg *eventstore func (c *Commands) createVerifyMFAU2FEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FTitle, existingText.VerifyMFAU2FTitle, text.VerifyMFAU2F.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FTitle, existingText.VerifyMFAU2FTitle, text.VerifyMFAU2F.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FDescription, existingText.VerifyMFAU2FDescription, text.VerifyMFAU2F.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FDescription, existingText.VerifyMFAU2FDescription, text.VerifyMFAU2F.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FValidateTokenText, existingText.VerifyMFAU2FValidateTokenText, text.VerifyMFAU2F.ValidateTokenButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FValidateTokenText, existingText.VerifyMFAU2FValidateTokenText, text.VerifyMFAU2F.ValidateTokenButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FNotSupported, existingText.VerifyMFAU2FNotSupported, text.VerifyMFAU2F.NotSupported, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FNotSupported, existingText.VerifyMFAU2FNotSupported, text.VerifyMFAU2F.NotSupported, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FErrorRetry, existingText.VerifyMFAU2FErrorRetry, text.VerifyMFAU2F.ErrorRetry, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyVerifyMFAU2FErrorRetry, existingText.VerifyMFAU2FErrorRetry, text.VerifyMFAU2F.ErrorRetry, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -566,27 +566,27 @@ func (c *Commands) createVerifyMFAU2FEvents(ctx context.Context, agg *eventstore func (c *Commands) createPasswordlessEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessTitle, existingText.PasswordlessTitle, text.Passwordless.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessTitle, existingText.PasswordlessTitle, text.Passwordless.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessDescription, existingText.PasswordlessDescription, text.Passwordless.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessDescription, existingText.PasswordlessDescription, text.Passwordless.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessLoginWithPwButtonText, existingText.PasswordlessLoginWithPwButtonText, text.Passwordless.LoginWithPwButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessLoginWithPwButtonText, existingText.PasswordlessLoginWithPwButtonText, text.Passwordless.LoginWithPwButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessValidateTokenButtonText, existingText.PasswordlessValidateTokenButtonText, text.Passwordless.ValidateTokenButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessValidateTokenButtonText, existingText.PasswordlessValidateTokenButtonText, text.Passwordless.ValidateTokenButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessNotSupported, existingText.PasswordlessNotSupported, text.Passwordless.NotSupported, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessNotSupported, existingText.PasswordlessNotSupported, text.Passwordless.NotSupported, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessErrorRetry, existingText.PasswordlessErrorRetry, text.Passwordless.ErrorRetry, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessErrorRetry, existingText.PasswordlessErrorRetry, text.Passwordless.ErrorRetry, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -595,27 +595,27 @@ func (c *Commands) createPasswordlessEvents(ctx context.Context, agg *eventstore func (c *Commands) createPasswordlessRegistrationEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationTitle, existingText.PasswordlessRegistrationTitle, text.PasswordlessRegistration.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationTitle, existingText.PasswordlessRegistrationTitle, text.PasswordlessRegistration.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDescription, existingText.PasswordlessRegistrationDescription, text.PasswordlessRegistration.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDescription, existingText.PasswordlessRegistrationDescription, text.PasswordlessRegistration.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, existingText.PasswordlessRegistrationRegisterTokenButtonText, text.PasswordlessRegistration.RegisterTokenButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, existingText.PasswordlessRegistrationRegisterTokenButtonText, text.PasswordlessRegistration.RegisterTokenButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, existingText.PasswordlessRegistrationTokenNameLabel, text.PasswordlessRegistration.TokenNameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, existingText.PasswordlessRegistrationTokenNameLabel, text.PasswordlessRegistration.TokenNameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationNotSupported, existingText.PasswordlessRegistrationNotSupported, text.PasswordlessRegistration.NotSupported, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationNotSupported, existingText.PasswordlessRegistrationNotSupported, text.PasswordlessRegistration.NotSupported, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationErrorRetry, existingText.PasswordlessRegistrationErrorRetry, text.PasswordlessRegistration.ErrorRetry, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationErrorRetry, existingText.PasswordlessRegistrationErrorRetry, text.PasswordlessRegistration.ErrorRetry, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -624,27 +624,27 @@ func (c *Commands) createPasswordlessRegistrationEvents(ctx context.Context, agg func (c *Commands) createPasswordlessPromptEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptTitle, existingText.PasswordlessPromptTitle, text.PasswordlessPrompt.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptTitle, existingText.PasswordlessPromptTitle, text.PasswordlessPrompt.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptDescription, existingText.PasswordlessPromptDescription, text.PasswordlessPrompt.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptDescription, existingText.PasswordlessPromptDescription, text.PasswordlessPrompt.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptDescriptionInit, existingText.PasswordlessPromptDescriptionInit, text.PasswordlessPrompt.DescriptionInit, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptDescriptionInit, existingText.PasswordlessPromptDescriptionInit, text.PasswordlessPrompt.DescriptionInit, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, existingText.PasswordlessPromptPasswordlessButtonText, text.PasswordlessPrompt.PasswordlessButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, existingText.PasswordlessPromptPasswordlessButtonText, text.PasswordlessPrompt.PasswordlessButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptNextButtonText, existingText.PasswordlessPromptNextButtonText, text.PasswordlessPrompt.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptNextButtonText, existingText.PasswordlessPromptNextButtonText, text.PasswordlessPrompt.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptSkipButtonText, existingText.PasswordlessPromptSkipButtonText, text.PasswordlessPrompt.SkipButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessPromptSkipButtonText, existingText.PasswordlessPromptSkipButtonText, text.PasswordlessPrompt.SkipButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -653,23 +653,23 @@ func (c *Commands) createPasswordlessPromptEvents(ctx context.Context, agg *even func (c *Commands) createPasswordlessRegistrationDoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneTitle, existingText.PasswordlessRegistrationDoneTitle, text.PasswordlessRegistrationDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneTitle, existingText.PasswordlessRegistrationDoneTitle, text.PasswordlessRegistrationDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneDescription, existingText.PasswordlessRegistrationDoneDescription, text.PasswordlessRegistrationDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneDescription, existingText.PasswordlessRegistrationDoneDescription, text.PasswordlessRegistrationDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, existingText.PasswordlessRegistrationDoneDescriptionClose, text.PasswordlessRegistrationDone.DescriptionClose, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, existingText.PasswordlessRegistrationDoneDescriptionClose, text.PasswordlessRegistrationDone.DescriptionClose, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, existingText.PasswordlessRegistrationDoneNextButtonText, text.PasswordlessRegistrationDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, existingText.PasswordlessRegistrationDoneNextButtonText, text.PasswordlessRegistrationDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, existingText.PasswordlessRegistrationDoneCancelButtonText, text.PasswordlessRegistrationDone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, existingText.PasswordlessRegistrationDoneCancelButtonText, text.PasswordlessRegistrationDone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -678,31 +678,31 @@ func (c *Commands) createPasswordlessRegistrationDoneEvents(ctx context.Context, func (c *Commands) createPasswordChangeEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeTitle, existingText.PasswordChangeTitle, text.PasswordChange.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeTitle, existingText.PasswordChangeTitle, text.PasswordChange.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDescription, existingText.PasswordChangeDescription, text.PasswordChange.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDescription, existingText.PasswordChangeDescription, text.PasswordChange.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeOldPasswordLabel, existingText.PasswordChangeOldPasswordLabel, text.PasswordChange.OldPasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeOldPasswordLabel, existingText.PasswordChangeOldPasswordLabel, text.PasswordChange.OldPasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNewPasswordLabel, existingText.PasswordChangeNewPasswordLabel, text.PasswordChange.NewPasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNewPasswordLabel, existingText.PasswordChangeNewPasswordLabel, text.PasswordChange.NewPasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, existingText.PasswordChangeNewPasswordConfirmLabel, text.PasswordChange.NewPasswordConfirmLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, existingText.PasswordChangeNewPasswordConfirmLabel, text.PasswordChange.NewPasswordConfirmLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeCancelButtonText, existingText.PasswordChangeCancelButtonText, text.PasswordChange.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeCancelButtonText, existingText.PasswordChangeCancelButtonText, text.PasswordChange.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNextButtonText, existingText.PasswordChangeNextButtonText, text.PasswordChange.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeNextButtonText, existingText.PasswordChangeNextButtonText, text.PasswordChange.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -711,15 +711,15 @@ func (c *Commands) createPasswordChangeEvents(ctx context.Context, agg *eventsto func (c *Commands) createPasswordChangeDoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneTitle, existingText.PasswordChangeDoneTitle, text.PasswordChangeDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneTitle, existingText.PasswordChangeDoneTitle, text.PasswordChangeDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneDescription, existingText.PasswordChangeDoneDescription, text.PasswordChangeDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneDescription, existingText.PasswordChangeDoneDescription, text.PasswordChangeDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneNextButtonText, existingText.PasswordChangeDoneNextButtonText, text.PasswordChangeDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordChangeDoneNextButtonText, existingText.PasswordChangeDoneNextButtonText, text.PasswordChangeDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -728,15 +728,15 @@ func (c *Commands) createPasswordChangeDoneEvents(ctx context.Context, agg *even func (c *Commands) createPasswordResetDoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneTitle, existingText.PasswordResetDoneTitle, text.PasswordResetDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneTitle, existingText.PasswordResetDoneTitle, text.PasswordResetDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneDescription, existingText.PasswordResetDoneDescription, text.PasswordResetDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneDescription, existingText.PasswordResetDoneDescription, text.PasswordResetDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneNextButtonText, existingText.PasswordResetDoneNextButtonText, text.PasswordResetDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyPasswordResetDoneNextButtonText, existingText.PasswordResetDoneNextButtonText, text.PasswordResetDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -745,23 +745,23 @@ func (c *Commands) createPasswordResetDoneEvents(ctx context.Context, agg *event func (c *Commands) createRegistrationOptionEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionTitle, existingText.RegistrationOptionTitle, text.RegisterOption.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionTitle, existingText.RegistrationOptionTitle, text.RegisterOption.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionDescription, existingText.RegistrationOptionDescription, text.RegisterOption.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionDescription, existingText.RegistrationOptionDescription, text.RegisterOption.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionUserNameButtonText, existingText.RegistrationOptionUserNameButtonText, text.RegisterOption.RegisterUsernamePasswordButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionUserNameButtonText, existingText.RegistrationOptionUserNameButtonText, text.RegisterOption.RegisterUsernamePasswordButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionExternalLoginDescription, existingText.RegistrationOptionExternalLoginDescription, text.RegisterOption.ExternalLoginDescription, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionExternalLoginDescription, existingText.RegistrationOptionExternalLoginDescription, text.RegisterOption.ExternalLoginDescription, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionLoginButtonText, existingText.RegistrationOptionLoginButtonText, text.RegisterOption.LoginButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationOptionLoginButtonText, existingText.RegistrationOptionLoginButtonText, text.RegisterOption.LoginButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -770,75 +770,75 @@ func (c *Commands) createRegistrationOptionEvents(ctx context.Context, agg *even func (c *Commands) createRegistrationUserEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTitle, existingText.RegistrationUserTitle, text.RegistrationUser.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTitle, existingText.RegistrationUserTitle, text.RegistrationUser.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserDescription, existingText.RegistrationUserDescription, text.RegistrationUser.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserDescription, existingText.RegistrationUserDescription, text.RegistrationUser.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserDescriptionOrgRegister, existingText.RegistrationUserDescriptionOrgRegister, text.RegistrationUser.DescriptionOrgRegister, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserDescriptionOrgRegister, existingText.RegistrationUserDescriptionOrgRegister, text.RegistrationUser.DescriptionOrgRegister, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserFirstnameLabel, existingText.RegistrationUserFirstnameLabel, text.RegistrationUser.FirstnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserFirstnameLabel, existingText.RegistrationUserFirstnameLabel, text.RegistrationUser.FirstnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserLastnameLabel, existingText.RegistrationUserLastnameLabel, text.RegistrationUser.LastnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserLastnameLabel, existingText.RegistrationUserLastnameLabel, text.RegistrationUser.LastnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserEmailLabel, existingText.RegistrationUserEmailLabel, text.RegistrationUser.EmailLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserEmailLabel, existingText.RegistrationUserEmailLabel, text.RegistrationUser.EmailLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserUsernameLabel, existingText.RegistrationUserUsernameLabel, text.RegistrationUser.UsernameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserUsernameLabel, existingText.RegistrationUserUsernameLabel, text.RegistrationUser.UsernameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserLanguageLabel, existingText.RegistrationUserLanguageLabel, text.RegistrationUser.LanguageLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserLanguageLabel, existingText.RegistrationUserLanguageLabel, text.RegistrationUser.LanguageLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserGenderLabel, existingText.RegistrationUserGenderLabel, text.RegistrationUser.GenderLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserGenderLabel, existingText.RegistrationUserGenderLabel, text.RegistrationUser.GenderLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPasswordLabel, existingText.RegistrationUserPasswordLabel, text.RegistrationUser.PasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPasswordLabel, existingText.RegistrationUserPasswordLabel, text.RegistrationUser.PasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPasswordConfirmLabel, existingText.RegistrationUserPasswordConfirmLabel, text.RegistrationUser.PasswordConfirmLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPasswordConfirmLabel, existingText.RegistrationUserPasswordConfirmLabel, text.RegistrationUser.PasswordConfirmLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, existingText.RegistrationUserTOSAndPrivacyLabel, text.RegistrationUser.TOSAndPrivacyLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, existingText.RegistrationUserTOSAndPrivacyLabel, text.RegistrationUser.TOSAndPrivacyLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSConfirm, existingText.RegistrationUserTOSConfirm, text.RegistrationUser.TOSConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSConfirm, existingText.RegistrationUserTOSConfirm, text.RegistrationUser.TOSConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSLinkText, existingText.RegistrationUserTOSLinkText, text.RegistrationUser.TOSLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserTOSLinkText, existingText.RegistrationUserTOSLinkText, text.RegistrationUser.TOSLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPrivacyConfirm, existingText.RegistrationUserPrivacyConfirm, text.RegistrationUser.PrivacyConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPrivacyConfirm, existingText.RegistrationUserPrivacyConfirm, text.RegistrationUser.PrivacyConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPrivacyLinkText, existingText.RegistrationUserPrivacyLinkText, text.RegistrationUser.PrivacyLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserPrivacyLinkText, existingText.RegistrationUserPrivacyLinkText, text.RegistrationUser.PrivacyLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserNextButtonText, existingText.RegistrationUserNextButtonText, text.RegistrationUser.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserNextButtonText, existingText.RegistrationUserNextButtonText, text.RegistrationUser.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserBackButtonText, existingText.RegistrationUserBackButtonText, text.RegistrationUser.BackButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegistrationUserBackButtonText, existingText.RegistrationUserBackButtonText, text.RegistrationUser.BackButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -847,67 +847,67 @@ func (c *Commands) createRegistrationUserEvents(ctx context.Context, agg *events func (c *Commands) createExternalRegistrationUserOverviewEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTitle, existingText.ExternalRegistrationUserOverviewTitle, text.ExternalRegistrationUserOverview.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTitle, existingText.ExternalRegistrationUserOverviewTitle, text.ExternalRegistrationUserOverview.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewDescription, existingText.ExternalRegistrationUserOverviewDescription, text.ExternalRegistrationUserOverview.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewDescription, existingText.ExternalRegistrationUserOverviewDescription, text.ExternalRegistrationUserOverview.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, existingText.ExternalRegistrationUserOverviewEmailLabel, text.ExternalRegistrationUserOverview.EmailLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, existingText.ExternalRegistrationUserOverviewEmailLabel, text.ExternalRegistrationUserOverview.EmailLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, existingText.ExternalRegistrationUserOverviewUsernameLabel, text.ExternalRegistrationUserOverview.UsernameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, existingText.ExternalRegistrationUserOverviewUsernameLabel, text.ExternalRegistrationUserOverview.UsernameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, existingText.ExternalRegistrationUserOverviewFirstnameLabel, text.ExternalRegistrationUserOverview.FirstnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, existingText.ExternalRegistrationUserOverviewFirstnameLabel, text.ExternalRegistrationUserOverview.FirstnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, existingText.ExternalRegistrationUserOverviewLastnameLabel, text.ExternalRegistrationUserOverview.LastnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, existingText.ExternalRegistrationUserOverviewLastnameLabel, text.ExternalRegistrationUserOverview.LastnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, existingText.ExternalRegistrationUserOverviewNicknameLabel, text.ExternalRegistrationUserOverview.NicknameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, existingText.ExternalRegistrationUserOverviewNicknameLabel, text.ExternalRegistrationUserOverview.NicknameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, existingText.ExternalRegistrationUserOverviewLanguageLabel, text.ExternalRegistrationUserOverview.LanguageLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, existingText.ExternalRegistrationUserOverviewLanguageLabel, text.ExternalRegistrationUserOverview.LanguageLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, existingText.ExternalRegistrationUserOverviewPhoneLabel, text.ExternalRegistrationUserOverview.PhoneLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, existingText.ExternalRegistrationUserOverviewPhoneLabel, text.ExternalRegistrationUserOverview.PhoneLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, existingText.ExternalRegistrationUserOverviewTOSAndPrivacyLabel, text.ExternalRegistrationUserOverview.TOSAndPrivacyLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, existingText.ExternalRegistrationUserOverviewTOSAndPrivacyLabel, text.ExternalRegistrationUserOverview.TOSAndPrivacyLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, existingText.ExternalRegistrationUserOverviewTOSConfirm, text.ExternalRegistrationUserOverview.TOSConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, existingText.ExternalRegistrationUserOverviewTOSConfirm, text.ExternalRegistrationUserOverview.TOSConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, existingText.ExternalRegistrationUserOverviewTOSLinkText, text.ExternalRegistrationUserOverview.TOSLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, existingText.ExternalRegistrationUserOverviewTOSLinkText, text.ExternalRegistrationUserOverview.TOSLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, existingText.ExternalRegistrationUserOverviewPrivacyConfirm, text.ExternalRegistrationUserOverview.PrivacyConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, existingText.ExternalRegistrationUserOverviewPrivacyConfirm, text.ExternalRegistrationUserOverview.PrivacyConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, existingText.ExternalRegistrationUserOverviewPrivacyLinkText, text.ExternalRegistrationUserOverview.PrivacyLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, existingText.ExternalRegistrationUserOverviewPrivacyLinkText, text.ExternalRegistrationUserOverview.PrivacyLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, existingText.ExternalRegistrationUserOverviewBackButtonText, text.ExternalRegistrationUserOverview.BackButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, existingText.ExternalRegistrationUserOverviewBackButtonText, text.ExternalRegistrationUserOverview.BackButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, existingText.ExternalRegistrationUserOverviewNextButtonText, text.ExternalRegistrationUserOverview.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, existingText.ExternalRegistrationUserOverviewNextButtonText, text.ExternalRegistrationUserOverview.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -916,63 +916,63 @@ func (c *Commands) createExternalRegistrationUserOverviewEvents(ctx context.Cont func (c *Commands) createRegistrationOrgEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTitle, existingText.RegisterOrgTitle, text.RegistrationOrg.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTitle, existingText.RegisterOrgTitle, text.RegistrationOrg.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgDescription, existingText.RegisterOrgDescription, text.RegistrationOrg.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgDescription, existingText.RegisterOrgDescription, text.RegistrationOrg.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgOrgNameLabel, existingText.RegisterOrgOrgNameLabel, text.RegistrationOrg.OrgNameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgOrgNameLabel, existingText.RegisterOrgOrgNameLabel, text.RegistrationOrg.OrgNameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgFirstnameLabel, existingText.RegisterOrgFirstnameLabel, text.RegistrationOrg.FirstnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgFirstnameLabel, existingText.RegisterOrgFirstnameLabel, text.RegistrationOrg.FirstnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgLastnameLabel, existingText.RegisterOrgLastnameLabel, text.RegistrationOrg.LastnameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgLastnameLabel, existingText.RegisterOrgLastnameLabel, text.RegistrationOrg.LastnameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgUsernameLabel, existingText.RegisterOrgUsernameLabel, text.RegistrationOrg.UsernameLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgUsernameLabel, existingText.RegisterOrgUsernameLabel, text.RegistrationOrg.UsernameLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgEmailLabel, existingText.RegisterOrgEmailLabel, text.RegistrationOrg.EmailLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgEmailLabel, existingText.RegisterOrgEmailLabel, text.RegistrationOrg.EmailLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPasswordLabel, existingText.RegisterOrgPasswordLabel, text.RegistrationOrg.PasswordLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPasswordLabel, existingText.RegisterOrgPasswordLabel, text.RegistrationOrg.PasswordLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPasswordConfirmLabel, existingText.RegisterOrgPasswordConfirmLabel, text.RegistrationOrg.PasswordConfirmLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPasswordConfirmLabel, existingText.RegisterOrgPasswordConfirmLabel, text.RegistrationOrg.PasswordConfirmLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, existingText.RegisterOrgTOSAndPrivacyLabel, text.RegistrationOrg.TOSAndPrivacyLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, existingText.RegisterOrgTOSAndPrivacyLabel, text.RegistrationOrg.TOSAndPrivacyLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSConfirm, existingText.RegisterOrgTOSConfirm, text.RegistrationOrg.TOSConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSConfirm, existingText.RegisterOrgTOSConfirm, text.RegistrationOrg.TOSConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSLinkText, existingText.RegisterOrgTOSLinkText, text.RegistrationOrg.TOSLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgTOSLinkText, existingText.RegisterOrgTOSLinkText, text.RegistrationOrg.TOSLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPrivacyConfirm, existingText.RegisterOrgPrivacyConfirm, text.RegistrationOrg.PrivacyConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPrivacyConfirm, existingText.RegisterOrgPrivacyConfirm, text.RegistrationOrg.PrivacyConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPrivacyLinkText, existingText.RegisterOrgPrivacyLinkText, text.RegistrationOrg.PrivacyLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgPrivacyLinkText, existingText.RegisterOrgPrivacyLinkText, text.RegistrationOrg.PrivacyLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgSaveButtonText, existingText.RegisterOrgSaveButtonText, text.RegistrationOrg.SaveButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyRegisterOrgSaveButtonText, existingText.RegisterOrgSaveButtonText, text.RegistrationOrg.SaveButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -981,19 +981,19 @@ func (c *Commands) createRegistrationOrgEvents(ctx context.Context, agg *eventst func (c *Commands) createLinkingUserEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneTitle, existingText.LinkingUserDoneTitle, text.LinkingUsersDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneTitle, existingText.LinkingUserDoneTitle, text.LinkingUsersDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneDescription, existingText.LinkingUserDoneDescription, text.LinkingUsersDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneDescription, existingText.LinkingUserDoneDescription, text.LinkingUsersDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneCancelButtonText, existingText.LinkingUserDoneCancelButtonText, text.LinkingUsersDone.CancelButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneCancelButtonText, existingText.LinkingUserDoneCancelButtonText, text.LinkingUsersDone.CancelButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneNextButtonText, existingText.LinkingUserDoneNextButtonText, text.LinkingUsersDone.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLinkingUserDoneNextButtonText, existingText.LinkingUserDoneNextButtonText, text.LinkingUsersDone.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -1002,39 +1002,39 @@ func (c *Commands) createLinkingUserEvents(ctx context.Context, agg *eventstore. func (c *Commands) createExternalUserNotFoundEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTitle, existingText.ExternalUserNotFoundTitle, text.ExternalNotFound.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTitle, existingText.ExternalUserNotFoundTitle, text.ExternalNotFound.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundDescription, existingText.ExternalUserNotFoundDescription, text.ExternalNotFound.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundDescription, existingText.ExternalUserNotFoundDescription, text.ExternalNotFound.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundLinkButtonText, existingText.ExternalUserNotFoundLinkButtonText, text.ExternalNotFound.LinkButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundLinkButtonText, existingText.ExternalUserNotFoundLinkButtonText, text.ExternalNotFound.LinkButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, existingText.ExternalUserNotFoundAutoRegisterButtonText, text.ExternalNotFound.AutoRegisterButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, existingText.ExternalUserNotFoundAutoRegisterButtonText, text.ExternalNotFound.AutoRegisterButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, existingText.ExternalUserNotFoundTOSAndPrivacyLabel, text.ExternalNotFound.TOSAndPrivacyLabel, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, existingText.ExternalUserNotFoundTOSAndPrivacyLabel, text.ExternalNotFound.TOSAndPrivacyLabel, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSConfirm, existingText.ExternalUserNotFoundTOSConfirm, text.ExternalNotFound.TOSConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSConfirm, existingText.ExternalUserNotFoundTOSConfirm, text.ExternalNotFound.TOSConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSLinkText, existingText.ExternalUserNotFoundTOSLinkText, text.ExternalNotFound.TOSLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundTOSLinkText, existingText.ExternalUserNotFoundTOSLinkText, text.ExternalNotFound.TOSLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundPrivacyConfirm, existingText.ExternalUserNotFoundPrivacyConfirm, text.ExternalNotFound.PrivacyConfirm, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundPrivacyConfirm, existingText.ExternalUserNotFoundPrivacyConfirm, text.ExternalNotFound.PrivacyConfirm, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundPrivacyLinkText, existingText.ExternalUserNotFoundPrivacyLinkText, text.ExternalNotFound.PrivacyLinkText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyExternalNotFoundPrivacyLinkText, existingText.ExternalUserNotFoundPrivacyLinkText, text.ExternalNotFound.PrivacyLinkText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -1043,19 +1043,19 @@ func (c *Commands) createExternalUserNotFoundEvents(ctx context.Context, agg *ev func (c *Commands) createSuccessLoginEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginTitle, existingText.SuccessLoginTitle, text.LoginSuccess.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginTitle, existingText.SuccessLoginTitle, text.LoginSuccess.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginAutoRedirectDescription, existingText.SuccessLoginAutoRedirectDescription, text.LoginSuccess.AutoRedirectDescription, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginAutoRedirectDescription, existingText.SuccessLoginAutoRedirectDescription, text.LoginSuccess.AutoRedirectDescription, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginRedirectedDescription, existingText.SuccessLoginRedirectedDescription, text.LoginSuccess.RedirectedDescription, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginRedirectedDescription, existingText.SuccessLoginRedirectedDescription, text.LoginSuccess.RedirectedDescription, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginNextButtonText, existingText.SuccessLoginNextButtonText, text.LoginSuccess.NextButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeySuccessLoginNextButtonText, existingText.SuccessLoginNextButtonText, text.LoginSuccess.NextButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -1064,15 +1064,15 @@ func (c *Commands) createSuccessLoginEvents(ctx context.Context, agg *eventstore func (c *Commands) createLogoutDoneEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneTitle, existingText.LogoutDoneTitle, text.LogoutDone.Title, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneTitle, existingText.LogoutDoneTitle, text.LogoutDone.Title, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneDescription, existingText.LogoutDoneDescription, text.LogoutDone.Description, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneDescription, existingText.LogoutDoneDescription, text.LogoutDone.Description, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneLoginButtonText, existingText.LogoutDoneLoginButtonText, text.LogoutDone.LoginButtonText, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyLogoutDoneLoginButtonText, existingText.LogoutDoneLoginButtonText, text.LogoutDone.LoginButtonText, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } @@ -1081,27 +1081,27 @@ func (c *Commands) createLogoutDoneEvents(ctx context.Context, agg *eventstore.A func (c *Commands) createFooterTextEvents(ctx context.Context, agg *eventstore.Aggregate, existingText *CustomLoginTextReadModel, text *domain.CustomLoginText, defaultText bool) []eventstore.Command { events := make([]eventstore.Command, 0) - event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterTOS, existingText.FooterTOS, text.Footer.TOS, text.Language, defaultText) + event := c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterTOS, existingText.FooterTOS, text.Footer.TOS, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterPrivacyPolicy, existingText.FooterPrivacyPolicy, text.Footer.PrivacyPolicy, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterPrivacyPolicy, existingText.FooterPrivacyPolicy, text.Footer.PrivacyPolicy, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterHelp, existingText.FooterHelp, text.Footer.Help, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterHelp, existingText.FooterHelp, text.Footer.Help, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterSupportEmail, existingText.FooterSupportEmail, text.Footer.SupportEmail, text.Language, defaultText) + event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterSupportEmail, existingText.FooterSupportEmail, text.Footer.SupportEmail, text.Language, defaultText, existingText.State) if event != nil { events = append(events, event) } return events } -func (c *Commands) createCustomLoginTextEvent(ctx context.Context, agg *eventstore.Aggregate, textKey, existingText, newText string, lang language.Tag, defaultText bool) eventstore.Command { - if existingText == newText { +func (c *Commands) createCustomLoginTextEvent(ctx context.Context, agg *eventstore.Aggregate, textKey, existingText, newText string, lang language.Tag, defaultText bool, policyState domain.PolicyState) eventstore.Command { + if existingText == newText && policyState == domain.PolicyStateActive { return nil } if defaultText { diff --git a/internal/command/org_custom_login_text_test.go b/internal/command/org_custom_login_text_test.go index 3c5cb207e..46fdcd5a5 100644 --- a/internal/command/org_custom_login_text_test.go +++ b/internal/command/org_custom_login_text_test.go @@ -801,6 +801,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, ), ), + eventFromEventPusher( + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, + ), + ), eventFromEventPusher( org.NewCustomTextSetEvent(context.Background(), &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, @@ -1372,6 +1377,7 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { Description: "Description", RegisterUsernamePasswordButtonText: "RegisterUsernamePasswordButtonText", ExternalLoginDescription: "ExternalLoginDescription", + LoginButtonText: "LoginButtonText", }, RegistrationUser: domain.RegistrationUserScreenText{ Title: "Title", From b9de36778fa31bbd5f42dea054555e67954cf821 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 7 Aug 2023 10:01:24 +0200 Subject: [PATCH 48/76] fix: check if session is reused on reauthentication (#6322) * fix: check if session is reused on reauth steps * add nolint directive --------- Co-authored-by: Silvan (cherry picked from commit 57857b8d30cc76f6d87198258bb936e9990911ba) --- .../eventsourcing/eventstore/auth_request.go | 91 +++++++++++-------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 0f81e6414..9a4bdfc99 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -547,6 +547,13 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR if err != nil { return nil, err } + // If there's no user, checks if the user could be reused (from the session). + // (the nextStepsUser will update the userID in the request in that case) + if request.UserID == "" { + if _, err = repo.nextStepsUser(request); err != nil { + return nil, err + } + } if request.UserID != userID { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-GBH32", "Errors.User.NotMatchingUserID") } @@ -913,50 +920,19 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(ctx context.Context, request return nil } -func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.AuthRequest, checkLoggedIn bool) ([]domain.NextStep, error) { +//nolint:gocognit +func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.AuthRequest, checkLoggedIn bool) (steps []domain.NextStep, err error) { if request == nil { return nil, errors.ThrowInvalidArgument(nil, "EVENT-ds27a", "Errors.Internal") } - steps := make([]domain.NextStep, 0) + steps = make([]domain.NextStep, 0) if !checkLoggedIn && domain.IsPrompt(request.Prompt, domain.PromptNone) { return append(steps, &domain.RedirectToCallbackStep{}), nil } if request.UserID == "" { - if request.LinkingUsers != nil && len(request.LinkingUsers) > 0 { - steps = append(steps, new(domain.ExternalNotFoundOptionStep)) - return steps, nil - } - if domain.IsPrompt(request.Prompt, domain.PromptCreate) { - return append(steps, &domain.RegistrationStep{}), nil - } - // if there's a login or consent prompt, but not select account, just return the login step - if len(request.Prompt) > 0 && !domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) { - return append(steps, new(domain.LoginStep)), nil - } else { - // if no user was specified, no prompt or select_account was provided, - // then check the active user sessions (of the user agent) - users, err := repo.usersForUserSelection(request) - if err != nil { - return nil, err - } - if domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) { - steps = append(steps, &domain.SelectUserStep{Users: users}) - } - if request.SelectedIDPConfigID != "" { - steps = append(steps, &domain.RedirectToExternalIDPStep{}) - } - if len(request.Prompt) == 0 && len(users) == 0 { - steps = append(steps, new(domain.LoginStep)) - } - // if no prompt was provided, but there are multiple user sessions, then the user must decide which to use - if len(request.Prompt) == 0 && len(users) > 1 { - steps = append(steps, &domain.SelectUserStep{Users: users}) - } - if len(steps) > 0 { - return steps, nil - } - // a single user session was found, use that automatically - request.UserID = users[0].UserID + steps, err = repo.nextStepsUser(request) + if err != nil || len(steps) > 0 { + return steps, err } } user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID, request.LoginPolicy.IgnoreUnknownUsernames) @@ -1046,6 +1022,47 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth return append(steps, &domain.RedirectToCallbackStep{}), nil } +func (repo *AuthRequestRepo) nextStepsUser(request *domain.AuthRequest) ([]domain.NextStep, error) { + steps := make([]domain.NextStep, 0) + if request.LinkingUsers != nil && len(request.LinkingUsers) > 0 { + steps = append(steps, new(domain.ExternalNotFoundOptionStep)) + return steps, nil + } + if domain.IsPrompt(request.Prompt, domain.PromptCreate) { + return append(steps, &domain.RegistrationStep{}), nil + } + // if there's a login or consent prompt, but not select account, just return the login step + if len(request.Prompt) > 0 && !domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) { + return append(steps, new(domain.LoginStep)), nil + } else { + // if no user was specified, no prompt or select_account was provided, + // then check the active user sessions (of the user agent) + users, err := repo.usersForUserSelection(request) + if err != nil { + return nil, err + } + if domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) { + steps = append(steps, &domain.SelectUserStep{Users: users}) + } + if request.SelectedIDPConfigID != "" { + steps = append(steps, &domain.RedirectToExternalIDPStep{}) + } + if len(request.Prompt) == 0 && len(users) == 0 { + steps = append(steps, new(domain.LoginStep)) + } + // if no prompt was provided, but there are multiple user sessions, then the user must decide which to use + if len(request.Prompt) == 0 && len(users) > 1 { + steps = append(steps, &domain.SelectUserStep{Users: users}) + } + if len(steps) > 0 { + return steps, nil + } + // a single user session was found, use that automatically + request.SetUserInfo(users[0].UserID, users[0].UserName, users[0].LoginName, users[0].DisplayName, users[0].AvatarKey, users[0].ResourceOwner) + } + return steps, nil +} + func checkExternalIDPsOfUser(ctx context.Context, idpUserLinksProvider idpUserLinksProvider, userID string) (*query.IDPUserLinks, error) { userIDQuery, err := query.NewIDPUserLinksUserIDSearchQuery(userID) if err != nil { From 60171f8e2b35e7aa0e3ea3205c7257a85a2aaa6b Mon Sep 17 00:00:00 2001 From: mffap Date: Fri, 18 Aug 2023 10:49:57 +0200 Subject: [PATCH 49/76] docs(oidc): device auth grant supported, typo (#6380) doc(oidc): device auth grant supported, typo Co-authored-by: Livio Spring Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> (cherry picked from commit f5f9b39fdf96be74033059443760a39f214b786b) --- docs/docs/apis/openidoauth/endpoints.mdx | 2 +- docs/docs/apis/openidoauth/grant-types.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/apis/openidoauth/endpoints.mdx b/docs/docs/apis/openidoauth/endpoints.mdx index 8e9b8b231..a3b4875cd 100644 --- a/docs/docs/apis/openidoauth/endpoints.mdx +++ b/docs/docs/apis/openidoauth/endpoints.mdx @@ -112,7 +112,7 @@ When your `response_type` was `code` and no error occurred, the following respon ### Successful implicit response -When your `response_type` was either `it_token` or `id_token token` and no error occurred, the following response will be returned: +When your `response_type` was either `id_token` or `id_token token` and no error occurred, the following response will be returned: | Property | Description | | ------------ | ------------------------------------------------------------------------------------- | diff --git a/docs/docs/apis/openidoauth/grant-types.md b/docs/docs/apis/openidoauth/grant-types.md index 560aa46aa..e6562941c 100644 --- a/docs/docs/apis/openidoauth/grant-types.md +++ b/docs/docs/apis/openidoauth/grant-types.md @@ -9,7 +9,7 @@ For a list of supported or unsupported `Grant Types` please have a look at the t | Authorization Code | yes | | Authorization Code with PKCE | yes | | Client Credentials | yes | -| Device Authorization | under consideration | +| Device Authorization | yes | | Implicit | yes | | JSON Web Token (JWT) Profile | yes | | Refresh Token | yes | From d029b82f0d821865ca176c8cae3682a8b9975dd4 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 18 Aug 2023 11:16:58 +0200 Subject: [PATCH 50/76] fix: handle metadata from post authentication on auto creation (#6389) (cherry picked from commit 90a62b777bd8b45aa59bfcbaeeeab1c864f92a08) --- internal/api/ui/login/external_provider_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index 7f32726d0..d895f1aef 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -410,7 +410,7 @@ func (l *Login) externalUserNotExisting(w http.ResponseWriter, r *http.Request, l.renderExternalNotFoundOption(w, r, authReq, orgIAMPolicy, human, idpLink, err) return } - if changed { + if changed || len(externalUser.Metadatas) > 0 { if err := l.authRepo.SetLinkingUser(r.Context(), authReq, externalUser); err != nil { l.renderError(w, r, authReq, err) return From 74841dfb1cc687cfc4c7466f7bf3f6c19bf51a4a Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 21 Aug 2023 14:21:45 +0200 Subject: [PATCH 51/76] fix: setMetadata in saml and pre access token triggers (#6398) (cherry picked from commit 84faf98bc28fce5e44c0e076d5fcea09aa64b9d3) --- internal/api/oidc/client.go | 3 ++- internal/api/saml/storage.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 8ce41d747..6a8dc6be6 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -712,7 +712,7 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG }), ), actions.SetFields("user", - actions.SetFields("setMetadata", func(call goja.FunctionCall) { + actions.SetFields("setMetadata", func(call goja.FunctionCall) goja.Value { if len(call.Arguments) != 2 { panic("exactly 2 (key, value) arguments expected") } @@ -733,6 +733,7 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG logging.WithError(err).Info("unable to set md in action") panic(err) } + return nil }), ), ), diff --git a/internal/api/saml/storage.go b/internal/api/saml/storage.go index 7a0d10f56..c5d348ccb 100644 --- a/internal/api/saml/storage.go +++ b/internal/api/saml/storage.go @@ -266,7 +266,7 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use }), ), actions.SetFields("user", - actions.SetFields("setMetadata", func(call goja.FunctionCall) { + actions.SetFields("setMetadata", func(call goja.FunctionCall) goja.Value { if len(call.Arguments) != 2 { panic("exactly 2 (key, value) arguments expected") } @@ -287,6 +287,7 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use logging.WithError(err).Info("unable to set md in action") panic(err) } + return nil }), ), ), From 7442492b8a98bfcf4f00034d92b57c3628af0ab2 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 21 Aug 2023 14:41:39 +0200 Subject: [PATCH 52/76] fix(login): add some missing translations for OTP (#6409) (cherry picked from commit e701e05d239d85dc40250ba2e70d21cff485955b) --- internal/api/ui/login/static/i18n/bg.yaml | 6 ++++++ internal/api/ui/login/static/i18n/de.yaml | 2 +- internal/api/ui/login/static/i18n/es.yaml | 7 +++++++ internal/api/ui/login/static/i18n/fr.yaml | 7 +++++++ internal/api/ui/login/static/i18n/it.yaml | 7 +++++++ internal/api/ui/login/static/i18n/ja.yaml | 7 +++++++ internal/api/ui/login/static/i18n/mk.yaml | 7 +++++++ internal/api/ui/login/static/i18n/pl.yaml | 7 +++++++ internal/api/ui/login/static/i18n/pt.yaml | 7 +++++++ internal/api/ui/login/static/i18n/zh.yaml | 7 +++++++ 10 files changed, 63 insertions(+), 1 deletion(-) diff --git a/internal/api/ui/login/static/i18n/bg.yaml b/internal/api/ui/login/static/i18n/bg.yaml index ab1ebe768..1a7413e57 100644 --- a/internal/api/ui/login/static/i18n/bg.yaml +++ b/internal/api/ui/login/static/i18n/bg.yaml @@ -137,6 +137,12 @@ VerifyMFAOTP: Description: Проверете вашия втори фактор CodeLabel: Код NextButtonText: следващия +VerifyOTP: + Title: Проверете 2-фактора + Description: Проверете вашия втори фактор + CodeLabel: Код + ResendButtonText: код за препращане + NextButtonText: следващия VerifyMFAU2F: Title: 2-факторна проверка Description: >- diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index eb39591ea..01b52da3e 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -138,13 +138,13 @@ VerifyMFAOTP: Title: 2-Faktor verifizieren Description: Verifiziere deinen Zweitfaktor CodeLabel: Code - ResendButtonText: Code erneut senden NextButtonText: next VerifyOTP: Title: 2-Faktor verifizieren Description: Verifiziere deinen Zweitfaktor CodeLabel: Code + ResendButtonText: Code erneut senden NextButtonText: next VerifyMFAU2F: diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml index 3d41001a5..7ddb5e743 100644 --- a/internal/api/ui/login/static/i18n/es.yaml +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Código NextButtonText: siguiente +VerifyOTP: + Title: Verificar doble factor + Description: Verifica tu doble factor + CodeLabel: Código + ResendButtonText: reenviar código + NextButtonText: siguiente + VerifyMFAU2F: Title: Verificación de doble factor Description: Verifica tu doble factor de autenticación con el dispositivo registrado (p.e FaceID, Windows Hello, Huella dactilar) diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index 95a58d282..e5a3ac38f 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Code NextButtonText: Suivant +VerifyOTP: + Title: Vérifier 2-Facteurs + Description: Vérifiez votre second facteur + CodeLabel: Code + ResendButtonText: Renvoyer le code + NextButtonText: Suivant + VerifyMFAU2F: Title: Vérifier 2-Facteurs Description: Vérifiez votre facteur 2 avec l'appareil enregistré (par exemple FaceID, Windows Hello, empreinte digitale). diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index 3ed763f3e..814ca3fcd 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Codice NextButtonText: Avanti +VerifyOTP: + Title: Verificazione fattore + Description: Verifica il tuo secondo fattore con la tua app + CodeLabel: Codice + ResendButtonText: Reinvia codice + NextButtonText: Avanti + VerifyMFAU2F: Title: Verificazione fattore Description: Verifica il tuo fattore con il dispositivo registrato (ad es. FaceID, Windows Hello, impronta digitale). diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index c43975f54..6a86bae7a 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -133,6 +133,13 @@ VerifyMFAOTP: CodeLabel: コード NextButtonText: 次へ +VerifyOTP: + Title: 二要素認証の検証 + Description: 二要素認証を検証します。 + CodeLabel: コード + ResendButtonText: コードを再送信 + NextButtonText: 次へ + VerifyMFAU2F: Title: 二要素認証 Description: 登録されたデバイスで二要素認証を実行します(FaceID、Windows Hello、指紋など) diff --git a/internal/api/ui/login/static/i18n/mk.yaml b/internal/api/ui/login/static/i18n/mk.yaml index e0a8c20d8..de345d013 100644 --- a/internal/api/ui/login/static/i18n/mk.yaml +++ b/internal/api/ui/login/static/i18n/mk.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Код NextButtonText: следно +VerifyOTP: + Title: Потврда на 2-факторска автентикација + Description: Потврдете ја 2-факторска автентикација + CodeLabel: Код + ResendButtonText: повторно испрати код + NextButtonText: следно + VerifyMFAU2F: Title: Потврда на 2-факторска автентикација Description: Потврдете ја вашата 2-факторска автентикација со регистрираниот уред (на пример FaceID, Windows Hello, отпечаток од прст) diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index 38c4b2860..e1f6cecb7 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Kod NextButtonText: dalej +VerifyOTP: + Title: Zweryfikuj 2-etapowe uwierzytelnianie + Description: Zweryfikuj swój drugi czynnik + CodeLabel: Kod + ResendButtonText: wyślij kod ponownie + NextButtonText: dalej + VerifyMFAU2F: Title: Weryfikacja 2-etapowego uwierzytelniania Description: Zweryfikuj swoje 2-etapowe uwierzytelnianie za pomocą zarejestrowanego urządzenia (np. FaceID, Windows Hello, odcisk palca) diff --git a/internal/api/ui/login/static/i18n/pt.yaml b/internal/api/ui/login/static/i18n/pt.yaml index 54d65fb1e..dc44c2c45 100644 --- a/internal/api/ui/login/static/i18n/pt.yaml +++ b/internal/api/ui/login/static/i18n/pt.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: Código NextButtonText: próximo +VerifyOTP: + Title: Verificar 2 fatores + Description: Verifique seu segundo fator + CodeLabel: Código + ResendButtonText: reenviar código + NextButtonText: próximo + VerifyMFAU2F: Title: Verificação de 2 fatores Description: Verifique seu 2 fatores com o dispositivo registrado (por exemplo, FaceID, Windows Hello, Impressão digital) diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index e2c0f2e43..30c352b43 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -140,6 +140,13 @@ VerifyMFAOTP: CodeLabel: 验证码 NextButtonText: 继续 +VerifyOTP: + Title: 验证2-Factor + Description: 验证你的第二个因素 + CodeLabel: 验证码 + ResendButtonText: 重发代码 + NextButtonText: 继续 + VerifyMFAU2F: Title: 验证2-Factor Description: 用注册的设备验证你的2-Factor(如FaceID、Windows Hello、Fingerprint)。 From 22af4dcd97f1991eb9134070295a2a80b3e5e569 Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 22 Aug 2023 14:49:02 +0200 Subject: [PATCH 53/76] feat(storage): read only transactions (#6417) feat(storage): read only transactions for queries (#6415) * fix: tests * bastle wie en grosse * fix(database): scan as callback * fix tests * fix merge failures * remove as of system time * refactor: remove unused test * refacotr: remove unused lines --- cmd/key/key.go | 2 +- cmd/setup/03.go | 4 +- cmd/setup/setup.go | 2 +- cmd/start/start.go | 2 +- .../repository/eventsourcing/view/view.go | 2 +- .../repository/eventsourcing/view/view.go | 2 +- .../auth_request/repository/cache/cache.go | 7 +- .../repository/eventsourcing/view/view.go | 2 +- internal/crypto/database/database.go | 69 ++++++------ internal/crypto/database/database_test.go | 33 +++++- internal/database/cockroach/config.go | 8 +- internal/database/cockroach/config_test.go | 61 ----------- internal/database/database.go | 63 +++++++++++ .../handler/crdb/current_sequence.go | 68 ++++++------ .../eventstore/handler/crdb/db_mock_test.go | 16 ++- .../eventstore/handler/crdb/handler_stmt.go | 68 ++++++++---- .../handler/crdb/handler_stmt_test.go | 37 ++++--- internal/eventstore/repository/sql/crdb.go | 25 +++-- internal/eventstore/repository/sql/query.go | 46 +++++--- .../eventstore/repository/sql/query_test.go | 17 ++- .../internal/repository/sql/db_mock_test.go | 14 +++ .../v1/internal/repository/sql/filter.go | 87 +++++++-------- .../v1/internal/repository/sql/filter_test.go | 3 + internal/logstore/emitters/access/database.go | 13 ++- .../logstore/emitters/execution/database.go | 12 ++- internal/query/action.go | 19 ++-- internal/query/action_flow.go | 37 ++++--- internal/query/action_flow_test.go | 12 +-- internal/query/action_test.go | 14 +-- internal/query/app.go | 101 +++++++++++------- internal/query/app_test.go | 16 +-- internal/query/auth_request.go | 14 ++- internal/query/auth_request_test.go | 2 +- internal/query/authn_key.go | 37 ++++--- internal/query/authn_key_test.go | 10 +- internal/query/certificate.go | 18 ++-- internal/query/certificate_test.go | 2 +- internal/query/current_sequence.go | 29 +++-- internal/query/current_sequence_test.go | 2 +- internal/query/custom_text.go | 20 ++-- internal/query/custom_text_test.go | 2 +- internal/query/device_auth.go | 16 ++- internal/query/device_auth_test.go | 6 ++ internal/query/domain_policy.go | 18 ++-- internal/query/domain_policy_test.go | 4 +- internal/query/failed_events.go | 7 +- internal/query/failed_events_test.go | 2 +- internal/query/iam_member.go | 11 +- internal/query/iam_member_test.go | 2 +- internal/query/idp.go | 18 ++-- internal/query/idp_login_policy_link.go | 12 +-- internal/query/idp_login_policy_link_test.go | 2 +- internal/query/idp_template.go | 18 ++-- internal/query/idp_template_test.go | 6 +- internal/query/idp_test.go | 6 +- internal/query/idp_user_link.go | 9 +- internal/query/idp_user_link_test.go | 2 +- internal/query/instance.go | 33 +++--- internal/query/instance_domain.go | 9 +- internal/query/instance_domain_test.go | 2 +- internal/query/instance_test.go | 4 +- internal/query/key.go | 23 ++-- internal/query/key_test.go | 4 +- internal/query/label_policy.go | 36 ++++--- internal/query/lockout_policy.go | 18 ++-- internal/query/lockout_policy_test.go | 4 +- internal/query/login_policy.go | 50 +++++---- internal/query/login_policy_test.go | 12 +-- internal/query/mail_template.go | 18 ++-- internal/query/message_text.go | 17 +-- internal/query/message_text_test.go | 4 +- internal/query/milestone.go | 20 ++-- internal/query/milestone_test.go | 2 +- internal/query/notification_policy.go | 18 ++-- internal/query/notification_policy_test.go | 4 +- internal/query/notification_provider.go | 9 +- internal/query/notification_provider_test.go | 4 +- internal/query/oidc_settings.go | 9 +- internal/query/oidc_settings_test.go | 4 +- internal/query/org.go | 44 +++++--- internal/query/org_domain.go | 10 +- internal/query/org_domain_test.go | 2 +- internal/query/org_member.go | 12 +-- internal/query/org_member_test.go | 2 +- internal/query/org_metadata.go | 21 ++-- internal/query/org_metadata_test.go | 6 +- internal/query/org_test.go | 10 +- internal/query/password_age_policy.go | 18 ++-- internal/query/password_age_policy_test.go | 4 +- internal/query/password_complexity_policy.go | 18 ++-- .../query/password_complexity_policy_test.go | 4 +- internal/query/prepare_test.go | 98 +++++++++++++---- internal/query/privacy_policy.go | 18 ++-- internal/query/privacy_policy_test.go | 4 +- internal/query/project.go | 18 ++-- internal/query/project_grant.go | 34 +++--- internal/query/project_grant_member.go | 12 +-- internal/query/project_grant_member_test.go | 2 +- internal/query/project_grant_test.go | 6 +- internal/query/project_member.go | 12 +-- internal/query/project_member_test.go | 2 +- internal/query/project_role.go | 31 +++--- internal/query/project_role_test.go | 2 +- internal/query/project_test.go | 6 +- internal/query/secret_generator_test.go | 6 +- internal/query/secret_generators.go | 18 ++-- internal/query/security_policy.go | 9 +- internal/query/session.go | 23 ++-- internal/query/sessions_test.go | 6 +- internal/query/sms.go | 33 +++--- internal/query/sms_test.go | 6 +- internal/query/smtp.go | 9 +- internal/query/smtp_test.go | 4 +- internal/query/user.go | 85 +++++++++------ internal/query/user_auth_method.go | 27 ++--- internal/query/user_auth_method_test.go | 4 +- internal/query/user_grant.go | 20 ++-- internal/query/user_grant_test.go | 6 +- internal/query/user_membership.go | 11 +- internal/query/user_membership_test.go | 2 +- internal/query/user_metadata.go | 20 ++-- internal/query/user_metadata_test.go | 6 +- internal/query/user_personal_access_token.go | 23 ++-- .../query/user_personal_access_token_test.go | 6 +- internal/query/user_test.go | 34 +++--- internal/view/repository/db_mock_test.go | 26 +++++ internal/view/repository/query.go | 11 ++ internal/view/repository/requests.go | 20 +++- 128 files changed, 1355 insertions(+), 897 deletions(-) delete mode 100644 internal/database/cockroach/config_test.go diff --git a/cmd/key/key.go b/cmd/key/key.go index 1ccde775e..02fa272a8 100644 --- a/cmd/key/key.go +++ b/cmd/key/key.go @@ -128,5 +128,5 @@ func keyStorage(config database.Config, masterKey string) (crypto.KeyStorage, er if err != nil { return nil, err } - return cryptoDB.NewKeyStorage(db.DB, masterKey) + return cryptoDB.NewKeyStorage(db, masterKey) } diff --git a/cmd/setup/03.go b/cmd/setup/03.go index 24b094226..73d32fcab 100644 --- a/cmd/setup/03.go +++ b/cmd/setup/03.go @@ -2,7 +2,6 @@ package setup import ( "context" - "database/sql" "fmt" "os" "strings" @@ -14,6 +13,7 @@ import ( "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/crypto" crypto_db "github.com/zitadel/zitadel/internal/crypto/database" + "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" ) @@ -30,7 +30,7 @@ type FirstInstance struct { smtpEncryptionKey *crypto.KeyConfig oidcEncryptionKey *crypto.KeyConfig masterKey string - db *sql.DB + db *database.DB es *eventstore.Eventstore defaults systemdefaults.SystemDefaults zitadelRoles []authz.RoleMapping diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index beba81611..8097b6164 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -77,7 +77,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.FirstInstance.smtpEncryptionKey = config.EncryptionKeys.SMTP steps.FirstInstance.oidcEncryptionKey = config.EncryptionKeys.OIDC steps.FirstInstance.masterKey = masterKey - steps.FirstInstance.db = dbClient.DB + steps.FirstInstance.db = dbClient steps.FirstInstance.es = eventstoreClient steps.FirstInstance.defaults = config.SystemDefaults steps.FirstInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings diff --git a/cmd/start/start.go b/cmd/start/start.go index d0df178c9..c8a5c20e3 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -124,7 +124,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error return fmt.Errorf("cannot start client for projection: %w", err) } - keyStorage, err := cryptoDB.NewKeyStorage(dbClient.DB, masterKey) + keyStorage, err := cryptoDB.NewKeyStorage(dbClient, masterKey) if err != nil { return fmt.Errorf("cannot start key storage: %w", err) } diff --git a/internal/admin/repository/eventsourcing/view/view.go b/internal/admin/repository/eventsourcing/view/view.go index 095e7c1df..9ede97281 100644 --- a/internal/admin/repository/eventsourcing/view/view.go +++ b/internal/admin/repository/eventsourcing/view/view.go @@ -15,7 +15,7 @@ type View struct { } func StartView(sqlClient *database.DB) (*View, error) { - gorm, err := gorm.Open("postgres", sqlClient) + gorm, err := gorm.Open("postgres", sqlClient.DB) if err != nil { return nil, err } diff --git a/internal/auth/repository/eventsourcing/view/view.go b/internal/auth/repository/eventsourcing/view/view.go index b65badf1e..08a9014d1 100644 --- a/internal/auth/repository/eventsourcing/view/view.go +++ b/internal/auth/repository/eventsourcing/view/view.go @@ -23,7 +23,7 @@ type View struct { } func StartView(sqlClient *database.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, idGenerator id.Generator, es eventstore.Eventstore) (*View, error) { - gorm, err := gorm.Open("postgres", sqlClient) + gorm, err := gorm.Open("postgres", sqlClient.DB) if err != nil { return nil, err } diff --git a/internal/auth_request/repository/cache/cache.go b/internal/auth_request/repository/cache/cache.go index 37bdb2276..10090ef28 100644 --- a/internal/auth_request/repository/cache/cache.go +++ b/internal/auth_request/repository/cache/cache.go @@ -59,7 +59,12 @@ func (c *AuthRequestCache) getAuthRequest(key, value, instanceID string) (*domai var b []byte var requestType domain.AuthRequestType query := fmt.Sprintf("SELECT request, request_type FROM auth.auth_requests WHERE instance_id = $1 and %s = $2", key) - err := c.client.QueryRow(query, instanceID, value).Scan(&b, &requestType) + err := c.client.QueryRow( + func(row *sql.Row) error { + return row.Scan(&b, &requestType) + }, + query, instanceID, value) + if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, caos_errs.ThrowNotFound(err, "CACHE-d24aD", "Errors.AuthRequest.NotFound") diff --git a/internal/authz/repository/eventsourcing/view/view.go b/internal/authz/repository/eventsourcing/view/view.go index c3ee5b79a..0b07cd2e0 100644 --- a/internal/authz/repository/eventsourcing/view/view.go +++ b/internal/authz/repository/eventsourcing/view/view.go @@ -19,7 +19,7 @@ type View struct { } func StartView(sqlClient *database.DB, idGenerator id.Generator, queries *query.Queries) (*View, error) { - gorm, err := gorm.Open("postgres", sqlClient) + gorm, err := gorm.Open("postgres", sqlClient.DB) if err != nil { return nil, err } diff --git a/internal/crypto/database/database.go b/internal/crypto/database/database.go index 228a9261b..7cbf46dc5 100644 --- a/internal/crypto/database/database.go +++ b/internal/crypto/database/database.go @@ -6,11 +6,12 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/zitadel/zitadel/internal/crypto" + z_db "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" ) type database struct { - client *sql.DB + client *z_db.DB masterKey string encrypt func(key, masterKey string) (encryptedKey string, err error) decrypt func(encryptedKey, masterKey string) (key string, err error) @@ -22,7 +23,7 @@ const ( encryptionKeysKeyCol = "key" ) -func NewKeyStorage(client *sql.DB, masterKey string) (*database, error) { +func NewKeyStorage(client *z_db.DB, masterKey string) (*database, error) { if err := checkMasterKeyLength(masterKey); err != nil { return nil, err } @@ -42,32 +43,30 @@ func (d *database) ReadKeys() (crypto.Keys, error) { if err != nil { return nil, caos_errs.ThrowInternal(err, "", "unable to read keys") } - rows, err := d.client.Query(stmt, args...) + err = d.client.Query(func(rows *sql.Rows) error { + for rows.Next() { + var id, encryptionKey string + err = rows.Scan(&id, &encryptionKey) + if err != nil { + return caos_errs.ThrowInternal(err, "", "unable to read keys") + } + key, err := d.decrypt(encryptionKey, d.masterKey) + if err != nil { + return caos_errs.ThrowInternal(err, "", "unable to decrypt key") + } + keys[id] = key + } + return nil + }, stmt, args...) + if err != nil { return nil, caos_errs.ThrowInternal(err, "", "unable to read keys") } - for rows.Next() { - var id, encryptionKey string - err = rows.Scan(&id, &encryptionKey) - if err != nil { - return nil, caos_errs.ThrowInternal(err, "", "unable to read keys") - } - key, err := d.decrypt(encryptionKey, d.masterKey) - if err != nil { - if err := rows.Close(); err != nil { - return nil, caos_errs.ThrowInternal(err, "", "unable to close rows") - } - return nil, caos_errs.ThrowInternal(err, "", "unable to decrypt key") - } - keys[id] = key - } - if err := rows.Close(); err != nil { - return nil, caos_errs.ThrowInternal(err, "", "unable to close rows") - } - return keys, err + + return keys, nil } -func (d *database) ReadKey(id string) (*crypto.Key, error) { +func (d *database) ReadKey(id string) (_ *crypto.Key, err error) { stmt, args, err := sq.Select(encryptionKeysKeyCol). From(EncryptionKeysTable). Where(sq.Eq{encryptionKeysIDCol: id}). @@ -76,19 +75,23 @@ func (d *database) ReadKey(id string) (*crypto.Key, error) { if err != nil { return nil, caos_errs.ThrowInternal(err, "", "unable to read key") } - row := d.client.QueryRow(stmt, args...) + var key string + err = d.client.QueryRow(func(row *sql.Row) error { + var encryptionKey string + err = row.Scan(&encryptionKey) + if err != nil { + return caos_errs.ThrowInternal(err, "", "unable to read key") + } + key, err = d.decrypt(encryptionKey, d.masterKey) + if err != nil { + return caos_errs.ThrowInternal(err, "", "unable to decrypt key") + } + return nil + }, stmt, args...) if err != nil { return nil, caos_errs.ThrowInternal(err, "", "unable to read key") } - var encryptionKey string - err = row.Scan(&encryptionKey) - if err != nil { - return nil, caos_errs.ThrowInternal(err, "", "unable to read key") - } - key, err := d.decrypt(encryptionKey, d.masterKey) - if err != nil { - return nil, caos_errs.ThrowInternal(err, "", "unable to decrypt key") - } + return &crypto.Key{ ID: id, Value: key, diff --git a/internal/crypto/database/database_test.go b/internal/crypto/database/database_test.go index b85c5f882..62a089ab3 100644 --- a/internal/crypto/database/database_test.go +++ b/internal/crypto/database/database_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/zitadel/zitadel/internal/crypto" + z_db "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" ) @@ -46,7 +47,7 @@ func Test_database_ReadKeys(t *testing.T) { { "decryption error", fields{ - client: dbMock(t, expectQuery( + client: dbMock(t, expectQueryScanErr( "SELECT id, key FROM system.encryption_keys", []string{"id", "key"}, [][]driver.Value{ @@ -172,7 +173,7 @@ func Test_database_ReadKey(t *testing.T) { { "key not found err", fields{ - client: dbMock(t, expectQuery( + client: dbMock(t, expectQueryScanErr( "SELECT key FROM system.encryption_keys WHERE id = $1", nil, nil, @@ -192,7 +193,7 @@ func Test_database_ReadKey(t *testing.T) { { "decryption error", fields{ - client: dbMock(t, expectQuery( + client: dbMock(t, expectQueryScanErr( "SELECT key FROM system.encryption_keys WHERE id = $1", []string{"key"}, [][]driver.Value{ @@ -445,7 +446,7 @@ func Test_checkMasterKeyLength(t *testing.T) { type db struct { mock sqlmock.Sqlmock - db *sql.DB + db *z_db.DB } func dbMock(t *testing.T, expectations ...func(m sqlmock.Sqlmock)) db { @@ -459,19 +460,41 @@ func dbMock(t *testing.T, expectations ...func(m sqlmock.Sqlmock)) db { } return db{ mock: mock, - db: client, + db: &z_db.DB{DB: client}, } } func expectQueryErr(query string, err error, args ...driver.Value) func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) { + m.ExpectBegin() m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnError(err) + m.ExpectRollback() + } +} + +func expectQueryScanErr(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) { + return func(m sqlmock.Sqlmock) { + m.ExpectBegin() + q := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...) + m.ExpectRollback() + result := sqlmock.NewRows(cols) + count := uint64(len(rows)) + for _, row := range rows { + if cols[len(cols)-1] == "count" { + row = append(row, count) + } + result.AddRow(row...) + } + q.WillReturnRows(result) + q.RowsWillBeClosed() } } func expectQuery(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) { + m.ExpectBegin() q := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...) + m.ExpectCommit() result := sqlmock.NewRows(cols) count := uint64(len(rows)) for _, row := range rows { diff --git a/internal/database/cockroach/config.go b/internal/database/cockroach/config.go index f48f026e2..ca51af16d 100644 --- a/internal/database/cockroach/config.go +++ b/internal/database/cockroach/config.go @@ -2,7 +2,6 @@ package cockroach import ( "database/sql" - "fmt" "strconv" "strings" "time" @@ -94,12 +93,7 @@ func (c *Config) Type() string { } func (c *Config) Timetravel(d time.Duration) string { - // verify that it is at least 1 micro second - if d < time.Microsecond { - d = time.Microsecond - } - - return fmt.Sprintf(" AS OF SYSTEM TIME '-%d µs' ", d.Microseconds()) + return "" } type User struct { diff --git a/internal/database/cockroach/config_test.go b/internal/database/cockroach/config_test.go deleted file mode 100644 index ca16f98f7..000000000 --- a/internal/database/cockroach/config_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package cockroach - -import ( - "testing" - "time" -) - -func TestConfig_Timetravel(t *testing.T) { - type args struct { - d time.Duration - } - tests := []struct { - name string - args args - want string - }{ - { - name: "no duration", - args: args{ - d: 0, - }, - want: " AS OF SYSTEM TIME '-1 µs' ", - }, - { - name: "less than microsecond", - args: args{ - d: 100 * time.Nanosecond, - }, - want: " AS OF SYSTEM TIME '-1 µs' ", - }, - { - name: "10 microseconds", - args: args{ - d: 10 * time.Microsecond, - }, - want: " AS OF SYSTEM TIME '-10 µs' ", - }, - { - name: "10 milliseconds", - args: args{ - d: 10 * time.Millisecond, - }, - want: " AS OF SYSTEM TIME '-10000 µs' ", - }, - { - name: "1 second", - args: args{ - d: 1 * time.Second, - }, - want: " AS OF SYSTEM TIME '-1000000 µs' ", - }, - } - for _, tt := range tests { - c := &Config{} - t.Run(tt.name, func(t *testing.T) { - if got := c.Timetravel(tt.args.d); got != tt.want { - t.Errorf("Config.Timetravel() = %q, want %q", got, tt.want) - } - }) - } -} diff --git a/internal/database/database.go b/internal/database/database.go index c7507f1e6..5f4de0122 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,9 +1,12 @@ package database import ( + "context" "database/sql" "reflect" + "github.com/zitadel/logging" + _ "github.com/zitadel/zitadel/internal/database/cockroach" "github.com/zitadel/zitadel/internal/database/dialect" _ "github.com/zitadel/zitadel/internal/database/postgres" @@ -24,6 +27,66 @@ type DB struct { dialect.Database } +func (db *DB) Query(scan func(*sql.Rows) error, query string, args ...any) error { + return db.QueryContext(context.Background(), scan, query, args...) +} + +func (db *DB) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) (err error) { + tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) + if err != nil { + return err + } + defer func() { + if err != nil { + rollbackErr := tx.Rollback() + logging.OnError(rollbackErr).Info("commit of read only transaction failed") + return + } + err = tx.Commit() + }() + + rows, err := tx.QueryContext(ctx, query, args...) + if err != nil { + return err + } + defer func() { + closeErr := rows.Close() + logging.OnError(closeErr).Info("rows.Close failed") + }() + + if err = scan(rows); err != nil { + return err + } + return rows.Err() +} + +func (db *DB) QueryRow(scan func(*sql.Row) error, query string, args ...any) (err error) { + return db.QueryRowContext(context.Background(), scan, query, args...) +} + +func (db *DB) QueryRowContext(ctx context.Context, scan func(row *sql.Row) error, query string, args ...any) (err error) { + tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) + if err != nil { + return err + } + defer func() { + if err != nil { + rollbackErr := tx.Rollback() + logging.OnError(rollbackErr).Info("commit of read only transaction failed") + return + } + err = tx.Commit() + }() + + row := tx.QueryRowContext(ctx, query, args...) + + err = scan(row) + if err != nil { + return err + } + return row.Err() +} + func Connect(config Config, useAdmin bool) (*DB, error) { client, err := config.connector.Connect(useAdmin) if err != nil { diff --git a/internal/eventstore/handler/crdb/current_sequence.go b/internal/eventstore/handler/crdb/current_sequence.go index b11c17dd4..2b92343be 100644 --- a/internal/eventstore/handler/crdb/current_sequence.go +++ b/internal/eventstore/handler/crdb/current_sequence.go @@ -12,9 +12,10 @@ import ( ) const ( - currentSequenceStmtFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2) FOR UPDATE` - updateCurrentSequencesStmtFormat = `INSERT INTO %s (projection_name, aggregate_type, current_sequence, instance_id, timestamp) VALUES ` - updateCurrentSequencesConflictStmt = ` ON CONFLICT (projection_name, aggregate_type, instance_id) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp` + currentSequenceStmtFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2) FOR UPDATE` + currentSequenceStmtWithoutLockFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2)` + updateCurrentSequencesStmtFormat = `INSERT INTO %s (projection_name, aggregate_type, current_sequence, instance_id, timestamp) VALUES ` + updateCurrentSequencesConflictStmt = ` ON CONFLICT (projection_name, aggregate_type, instance_id) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp` ) type currentSequences map[eventstore.AggregateType][]*instanceSequence @@ -24,41 +25,38 @@ type instanceSequence struct { sequence uint64 } -func (h *StatementHandler) currentSequences(ctx context.Context, query func(context.Context, string, ...interface{}) (*sql.Rows, error), instanceIDs database.StringArray) (currentSequences, error) { - rows, err := query(ctx, h.currentSequenceStmt, h.ProjectionName, instanceIDs) +func (h *StatementHandler) currentSequences(ctx context.Context, isTx bool, query func(context.Context, func(*sql.Rows) error, string, ...interface{}) error, instanceIDs database.StringArray) (currentSequences, error) { + stmt := h.currentSequenceStmt + if !isTx { + stmt = h.currentSequenceWithoutLockStmt + } + + sequences := make(currentSequences, len(h.aggregates)) + err := query(ctx, + func(rows *sql.Rows) error { + for rows.Next() { + var ( + aggregateType eventstore.AggregateType + sequence uint64 + instanceID string + ) + + err := rows.Scan(&sequence, &aggregateType, &instanceID) + if err != nil { + return errors.ThrowInternal(err, "CRDB-dbatK", "scan failed") + } + + sequences[aggregateType] = append(sequences[aggregateType], &instanceSequence{ + sequence: sequence, + instanceID: instanceID, + }) + } + return nil + }, + stmt, h.ProjectionName, instanceIDs) if err != nil { return nil, err } - - defer rows.Close() - - sequences := make(currentSequences, len(h.aggregates)) - for rows.Next() { - var ( - aggregateType eventstore.AggregateType - sequence uint64 - instanceID string - ) - - err = rows.Scan(&sequence, &aggregateType, &instanceID) - if err != nil { - return nil, errors.ThrowInternal(err, "CRDB-dbatK", "scan failed") - } - - sequences[aggregateType] = append(sequences[aggregateType], &instanceSequence{ - sequence: sequence, - instanceID: instanceID, - }) - } - - if err = rows.Close(); err != nil { - return nil, errors.ThrowInternal(err, "CRDB-h5i5m", "close rows failed") - } - - if err = rows.Err(); err != nil { - return nil, errors.ThrowInternal(err, "CRDB-O8zig", "errors in scanning rows") - } - return sequences, nil } diff --git a/internal/eventstore/handler/crdb/db_mock_test.go b/internal/eventstore/handler/crdb/db_mock_test.go index 33c7984e1..20b70cf5e 100644 --- a/internal/eventstore/handler/crdb/db_mock_test.go +++ b/internal/eventstore/handler/crdb/db_mock_test.go @@ -124,13 +124,17 @@ func expectSavePointRelease() func(sqlmock.Sqlmock) { } } -func expectCurrentSequence(tableName, projection string, seq uint64, aggregateType string, instanceIDs []string) func(sqlmock.Sqlmock) { +func expectCurrentSequence(isTx bool, tableName, projection string, seq uint64, aggregateType string, instanceIDs []string) func(sqlmock.Sqlmock) { rows := sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}) for _, instanceID := range instanceIDs { rows.AddRow(seq, aggregateType, instanceID) } return func(m sqlmock.Sqlmock) { - m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). + stmt := `SELECT current_sequence, aggregate_type, instance_id FROM ` + tableName + ` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\)` + if isTx { + stmt += " FOR UPDATE" + } + m.ExpectQuery(stmt). WithArgs( projection, database.StringArray(instanceIDs), @@ -141,9 +145,13 @@ func expectCurrentSequence(tableName, projection string, seq uint64, aggregateTy } } -func expectCurrentSequenceErr(tableName, projection string, instanceIDs []string, err error) func(sqlmock.Sqlmock) { +func expectCurrentSequenceErr(isTx bool, tableName, projection string, instanceIDs []string, err error) func(sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) { - m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). + stmt := `SELECT current_sequence, aggregate_type, instance_id FROM ` + tableName + ` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\)` + if isTx { + stmt += " FOR UPDATE" + } + m.ExpectQuery(stmt). WithArgs( projection, database.StringArray(instanceIDs), diff --git a/internal/eventstore/handler/crdb/handler_stmt.go b/internal/eventstore/handler/crdb/handler_stmt.go index e49fe007b..27418bf33 100644 --- a/internal/eventstore/handler/crdb/handler_stmt.go +++ b/internal/eventstore/handler/crdb/handler_stmt.go @@ -36,13 +36,14 @@ type StatementHandler struct { *handler.ProjectionHandler Locker - client *database.DB - sequenceTable string - currentSequenceStmt string - updateSequencesBaseStmt string - maxFailureCount uint - failureCountStmt string - setFailureCountStmt string + client *database.DB + sequenceTable string + currentSequenceStmt string + currentSequenceWithoutLockStmt string + updateSequencesBaseStmt string + maxFailureCount uint + failureCountStmt string + setFailureCountStmt string aggregates []eventstore.AggregateType reduces map[eventstore.EventType]handler.Reduce @@ -77,20 +78,21 @@ func NewStatementHandler( } h := StatementHandler{ - client: config.Client, - sequenceTable: config.SequenceTable, - maxFailureCount: config.MaxFailureCount, - currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, config.SequenceTable), - updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, config.SequenceTable), - failureCountStmt: fmt.Sprintf(failureCountStmtFormat, config.FailedEventsTable), - setFailureCountStmt: fmt.Sprintf(setFailureCountStmtFormat, config.FailedEventsTable), - aggregates: aggregateTypes, - reduces: reduces, - bulkLimit: config.BulkLimit, - Locker: NewLocker(config.Client.DB, config.LockTable, config.ProjectionName), - initCheck: config.InitCheck, - initialized: make(chan bool), - reduceScheduledPseudoEvent: reduceScheduledPseudoEvent, + client: config.Client, + sequenceTable: config.SequenceTable, + maxFailureCount: config.MaxFailureCount, + currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, config.SequenceTable), + currentSequenceWithoutLockStmt: fmt.Sprintf(currentSequenceStmtWithoutLockFormat, config.SequenceTable), + updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, config.SequenceTable), + failureCountStmt: fmt.Sprintf(failureCountStmtFormat, config.FailedEventsTable), + setFailureCountStmt: fmt.Sprintf(setFailureCountStmtFormat, config.FailedEventsTable), + aggregates: aggregateTypes, + reduces: reduces, + bulkLimit: config.BulkLimit, + Locker: NewLocker(config.Client.DB, config.LockTable, config.ProjectionName), + initCheck: config.InitCheck, + initialized: make(chan bool), + reduceScheduledPseudoEvent: reduceScheduledPseudoEvent, } h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.searchQuery, h.Lock, h.Unlock, h.initialized, reduceScheduledPseudoEvent) @@ -114,7 +116,7 @@ func (h *StatementHandler) searchQuery(ctx context.Context, instanceIDs []string } func (h *StatementHandler) dbSearchQuery(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) { - sequences, err := h.currentSequences(ctx, h.client.QueryContext, instanceIDs) + sequences, err := h.currentSequences(ctx, false, h.client.QueryContext, instanceIDs) if err != nil { return nil, 0, err } @@ -140,6 +142,26 @@ func (h *StatementHandler) dbSearchQuery(ctx context.Context, instanceIDs []stri return queryBuilder, h.bulkLimit, nil } +type transaction struct { + *sql.Tx +} + +func (t *transaction) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) error { + rows, err := t.Tx.QueryContext(ctx, query, args...) + if err != nil { + return err + } + defer func() { + closeErr := rows.Close() + logging.OnError(closeErr).Info("rows.Close failed") + }() + + if err = scan(rows); err != nil { + return err + } + return rows.Err() +} + // Update implements handler.Update func (h *StatementHandler) Update(ctx context.Context, stmts []*handler.Statement, reduce handler.Reduce) (index int, err error) { if len(stmts) == 0 { @@ -154,7 +176,7 @@ func (h *StatementHandler) Update(ctx context.Context, stmts []*handler.Statemen return -1, errors.ThrowInternal(err, "CRDB-e89Gq", "begin failed") } - sequences, err := h.currentSequences(ctx, tx.QueryContext, instanceIDs) + sequences, err := h.currentSequences(ctx, true, (&transaction{Tx: tx}).QueryContext, instanceIDs) if err != nil { tx.Rollback() return -1, err diff --git a/internal/eventstore/handler/crdb/handler_stmt_test.go b/internal/eventstore/handler/crdb/handler_stmt_test.go index b72db40eb..027469e71 100644 --- a/internal/eventstore/handler/crdb/handler_stmt_test.go +++ b/internal/eventstore/handler/crdb/handler_stmt_test.go @@ -90,7 +90,9 @@ func TestProjectionHandler_SearchQuery(t *testing.T) { return errors.Is(err, sql.ErrTxDone) }, expectations: []mockExpectation{ - expectCurrentSequenceErr("my_sequences", "my_projection", []string{"instanceID1"}, sql.ErrTxDone), + expectBegin(), + expectCurrentSequenceErr(false, "my_sequences", "my_projection", []string{"instanceID1"}, sql.ErrTxDone), + expectRollback(), }, SearchQueryBuilder: nil, }, @@ -112,7 +114,9 @@ func TestProjectionHandler_SearchQuery(t *testing.T) { return err == nil }, expectations: []mockExpectation{ - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1"}), + expectBegin(), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1"}), + expectCommit(), }, SearchQueryBuilder: eventstore. NewSearchQueryBuilder(eventstore.ColumnsEvent). @@ -142,7 +146,9 @@ func TestProjectionHandler_SearchQuery(t *testing.T) { return err == nil }, expectations: []mockExpectation{ - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1", "instanceID2"}), + expectBegin(), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1", "instanceID2"}), + expectCommit(), }, SearchQueryBuilder: eventstore. NewSearchQueryBuilder(eventstore.ColumnsEvent). @@ -216,6 +222,7 @@ func TestProjectionHandler_SearchQuery(t *testing.T) { t.Errorf("ProjectionHandler.prepareBulkStmts() error = %v", err) return } + if !reflect.DeepEqual(query, tt.want.SearchQueryBuilder) { t.Errorf("unexpected query: expected %v, got %v", tt.want.SearchQueryBuilder, query) } @@ -289,7 +296,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequenceErr("my_sequences", "my_projection", []string{"instanceID"}, sql.ErrTxDone), + expectCurrentSequenceErr(false, "my_sequences", "my_projection", []string{"instanceID"}, sql.ErrTxDone), expectRollback(), }, isErr: func(err error) bool { @@ -321,7 +328,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), expectRollback(), }, isErr: func(err error) bool { @@ -360,7 +367,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), expectCommit(), }, isErr: func(err error) bool { @@ -399,7 +406,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), expectSavePoint(), expectCreate("my_projection", []string{"col"}, []string{"$1"}), expectSavePointRelease(), @@ -442,7 +449,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), expectSavePoint(), expectCreate("my_projection", []string{"col"}, []string{"$1"}), expectSavePointRelease(), @@ -478,7 +485,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), expectCommit(), }, @@ -511,7 +518,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), expectCommit(), }, @@ -551,7 +558,7 @@ func TestStatementHandler_Update(t *testing.T) { want: want{ expectations: []mockExpectation{ expectBegin(), - expectCurrentSequence("my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), + expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), expectCommit(), }, @@ -1425,7 +1432,7 @@ func TestStatementHandler_currentSequence(t *testing.T) { return errors.Is(err, sql.ErrConnDone) }, expectations: []mockExpectation{ - expectCurrentSequenceErr("my_table", "my_projection", nil, sql.ErrConnDone), + expectCurrentSequenceErr(true, "my_table", "my_projection", nil, sql.ErrConnDone), }, }, }, @@ -1487,7 +1494,7 @@ func TestStatementHandler_currentSequence(t *testing.T) { return errors.Is(err, nil) }, expectations: []mockExpectation{ - expectCurrentSequence("my_table", "my_projection", 5, "agg", []string{"instanceID"}), + expectCurrentSequence(true, "my_table", "my_projection", 5, "agg", []string{"instanceID"}), }, sequences: currentSequences{ "agg": []*instanceSequence{ @@ -1515,7 +1522,7 @@ func TestStatementHandler_currentSequence(t *testing.T) { return errors.Is(err, nil) }, expectations: []mockExpectation{ - expectCurrentSequence("my_table", "my_projection", 5, "agg", []string{"instanceID1", "instanceID2"}), + expectCurrentSequence(true, "my_table", "my_projection", 5, "agg", []string{"instanceID1", "instanceID2"}), }, sequences: currentSequences{ "agg": []*instanceSequence{ @@ -1563,7 +1570,7 @@ func TestStatementHandler_currentSequence(t *testing.T) { t.Fatalf("unexpected err in begin: %v", err) } - seq, err := h.currentSequences(context.Background(), tx.QueryContext, tt.args.instanceIDs) + seq, err := h.currentSequences(context.Background(), true, (&transaction{Tx: tx}).QueryContext, tt.args.instanceIDs) if !tt.want.isErr(err) { t.Errorf("unexpected error: %v", err) } diff --git a/internal/eventstore/repository/sql/crdb.go b/internal/eventstore/repository/sql/crdb.go index 62cf1f92a..14fab23b6 100644 --- a/internal/eventstore/repository/sql/crdb.go +++ b/internal/eventstore/repository/sql/crdb.go @@ -161,13 +161,18 @@ func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueCons var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`) func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error { - row := db.QueryRowContext(ctx, "SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID) - if row.Err() != nil { - return caos_errs.ThrowInvalidArgument(row.Err(), "SQL-7gtFA", "Errors.InvalidArgument") - } var sequenceName string - if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) { - return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument") + err := db.QueryRowContext(ctx, + func(row *sql.Row) error { + if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) { + return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument") + } + return nil + }, + "SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID, + ) + if err != nil { + return err } if _, err := db.ExecContext(ctx, "CREATE SEQUENCE "+sequenceName); err != nil { @@ -220,9 +225,9 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC } // Filter returns all events matching the given search query -func (db *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery) (events []*repository.Event, err error) { +func (crdb *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery) (events []*repository.Event, err error) { events = []*repository.Event{} - err = query(ctx, db, searchQuery, &events) + err = query(ctx, crdb, searchQuery, &events) if err != nil { return nil, err } @@ -250,8 +255,8 @@ func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *repository.SearchQ return ids, nil } -func (db *CRDB) db() *sql.DB { - return db.DB.DB +func (db *CRDB) db() *database.DB { + return db.DB } func (db *CRDB) orderByEventSequence(desc bool) string { diff --git a/internal/eventstore/repository/sql/query.go b/internal/eventstore/repository/sql/query.go index 2457a0638..8bc3b3ac9 100644 --- a/internal/eventstore/repository/sql/query.go +++ b/internal/eventstore/repository/sql/query.go @@ -11,6 +11,7 @@ import ( "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/api/call" + "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/database/dialect" z_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/repository" @@ -24,13 +25,33 @@ type querier interface { eventQuery() string maxSequenceQuery() string instanceIDsQuery() string - db() *sql.DB + db() *database.DB orderByEventSequence(desc bool) string dialect.Database } type scan func(dest ...interface{}) error +type tx struct { + *sql.Tx +} + +func (t *tx) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) error { + rows, err := t.Tx.QueryContext(ctx, query, args...) + if err != nil { + return err + } + defer func() { + closeErr := rows.Close() + logging.OnError(closeErr).Info("rows.Close failed") + }() + + if err = scan(rows); err != nil { + return err + } + return rows.Err() +} + func query(ctx context.Context, criteria querier, searchQuery *repository.SearchQuery, dest interface{}) error { query, rowScanner := prepareColumns(criteria, searchQuery.Columns) where, values := prepareCondition(criteria, searchQuery.Filters) @@ -56,26 +77,27 @@ func query(ctx context.Context, criteria querier, searchQuery *repository.Search query = criteria.placeholder(query) var contextQuerier interface { - QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryContext(context.Context, func(rows *sql.Rows) error, string, ...interface{}) error } contextQuerier = criteria.db() if searchQuery.Tx != nil { - contextQuerier = searchQuery.Tx + contextQuerier = &tx{Tx: searchQuery.Tx} } - rows, err := contextQuerier.QueryContext(ctx, query, values...) + err := contextQuerier.QueryContext(ctx, + func(rows *sql.Rows) error { + for rows.Next() { + err := rowScanner(rows.Scan, dest) + if err != nil { + return err + } + } + return nil + }, query, values...) if err != nil { logging.New().WithError(err).Info("query failed") return z_errors.ThrowInternal(err, "SQL-KyeAx", "unable to filter events") } - defer rows.Close() - - for rows.Next() { - err = rowScanner(rows.Scan, dest) - if err != nil { - return err - } - } return nil } diff --git a/internal/eventstore/repository/sql/query_test.go b/internal/eventstore/repository/sql/query_test.go index af9aa9860..d7438ed27 100644 --- a/internal/eventstore/repository/sql/query_test.go +++ b/internal/eventstore/repository/sql/query_test.go @@ -741,7 +741,7 @@ func Test_query_events_mocked(t *testing.T) { }, }, fields: fields{ - mock: newMockClient(t).expectQuery(t, + mock: newMockClient(t).expectQueryScanErr(t, `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, []driver.Value{repository.AggregateType("user")}, &repository.Event{Sequence: 100}), @@ -853,7 +853,21 @@ type dbMock struct { } func (m *dbMock) expectQuery(t *testing.T, expectedQuery string, args []driver.Value, events ...*repository.Event) *dbMock { + m.mock.ExpectBegin() query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...) + m.mock.ExpectCommit() + rows := sqlmock.NewRows([]string{"event_sequence"}) + for _, event := range events { + rows = rows.AddRow(event.Sequence) + } + query.WillReturnRows(rows).RowsWillBeClosed() + return m +} + +func (m *dbMock) expectQueryScanErr(t *testing.T, expectedQuery string, args []driver.Value, events ...*repository.Event) *dbMock { + m.mock.ExpectBegin() + query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...) + m.mock.ExpectRollback() rows := sqlmock.NewRows([]string{"event_sequence"}) for _, event := range events { rows = rows.AddRow(event.Sequence) @@ -863,6 +877,7 @@ func (m *dbMock) expectQuery(t *testing.T, expectedQuery string, args []driver.V } func (m *dbMock) expectQueryErr(t *testing.T, expectedQuery string, args []driver.Value, err error) *dbMock { + m.mock.ExpectBegin() m.mock.ExpectQuery(expectedQuery).WithArgs(args...).WillReturnError(err) return m } diff --git a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go b/internal/eventstore/v1/internal/repository/sql/db_mock_test.go index e1271fc0d..da64dfd5d 100644 --- a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go +++ b/internal/eventstore/v1/internal/repository/sql/db_mock_test.go @@ -127,9 +127,11 @@ func (db *dbMock) expectFilterEventsLimit(aggregateType string, limit uint64, ev for i := 0; i < eventCount; i++ { rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") } + db.mock.ExpectBegin() db.mock.ExpectQuery(expectedFilterEventsLimitFormat). WithArgs(aggregateType, limit). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -138,8 +140,10 @@ func (db *dbMock) expectFilterEventsDesc(aggregateType string, eventCount int) * for i := eventCount; i > 0; i-- { rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") } + db.mock.ExpectBegin() db.mock.ExpectQuery(expectedFilterEventsDescFormat). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -148,9 +152,11 @@ func (db *dbMock) expectFilterEventsAggregateIDLimit(aggregateType, aggregateID for i := limit; i > 0; i-- { rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") } + db.mock.ExpectBegin() db.mock.ExpectQuery(expectedFilterEventsAggregateIDLimit). WithArgs(aggregateType, aggregateID, limit). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -159,28 +165,36 @@ func (db *dbMock) expectFilterEventsAggregateIDTypeLimit(aggregateType, aggregat for i := limit; i > 0; i-- { rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") } + db.mock.ExpectBegin() db.mock.ExpectQuery(expectedFilterEventsAggregateIDTypeLimit). WithArgs(aggregateType, aggregateID, limit). WillReturnRows(rows) + db.mock.ExpectCommit() return db } func (db *dbMock) expectFilterEventsError(returnedErr error) *dbMock { + db.mock.ExpectBegin() db.mock.ExpectQuery(expectedGetAllEvents). WillReturnError(returnedErr) + db.mock.ExpectRollback() return db } func (db *dbMock) expectLatestSequenceFilter(aggregateType string, sequence Sequence) *dbMock { + db.mock.ExpectBegin() db.mock.ExpectQuery(`SELECT MAX\(event_sequence\) FROM eventstore\.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \)`). WithArgs(aggregateType). WillReturnRows(sqlmock.NewRows([]string{"max_sequence"}).AddRow(sequence)) + db.mock.ExpectCommit() return db } func (db *dbMock) expectLatestSequenceFilterError(aggregateType string, err error) *dbMock { + db.mock.ExpectBegin() db.mock.ExpectQuery(`SELECT MAX\(event_sequence\) FROM eventstore\.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \)`). WithArgs(aggregateType).WillReturnError(err) + // db.mock.ExpectRollback() return db } diff --git a/internal/eventstore/v1/internal/repository/sql/filter.go b/internal/eventstore/v1/internal/repository/sql/filter.go index ab67730b5..850a1ee30 100644 --- a/internal/eventstore/v1/internal/repository/sql/filter.go +++ b/internal/eventstore/v1/internal/repository/sql/filter.go @@ -3,12 +3,13 @@ package sql import ( "context" "database/sql" + "errors" "runtime/debug" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" + errs "github.com/zitadel/zitadel/internal/errors" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -24,72 +25,74 @@ func (db *SQL) Filter(ctx context.Context, searchQuery *es_models.SearchQueryFac return db.filter(ctx, db.client, searchQuery) } -func (sql *SQL) filter(ctx context.Context, db *database.DB, searchQuery *es_models.SearchQueryFactory) (events []*es_models.Event, err error) { - query, limit, values, rowScanner := sql.buildQuery(ctx, db, searchQuery) +func (server *SQL) filter(ctx context.Context, db *database.DB, searchQuery *es_models.SearchQueryFactory) (events []*es_models.Event, err error) { + query, limit, values, rowScanner := server.buildQuery(ctx, db, searchQuery) if query == "" { - return nil, errors.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") + return nil, errs.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") } - rows, err := db.Query(query, values...) - if err != nil { - logging.New().WithError(err).Info("query failed") - return nil, errors.ThrowInternal(err, "SQL-IJuyR", "unable to filter events") - } - defer rows.Close() - events = make([]*es_models.Event, 0, limit) + err = db.QueryContext(ctx, + func(rows *sql.Rows) error { + for rows.Next() { + event := new(es_models.Event) + err := rowScanner(rows.Scan, event) + if err != nil { + return err + } - for rows.Next() { - event := new(es_models.Event) - err := rowScanner(rows.Scan, event) - if err != nil { - return nil, err - } - - events = append(events, event) + events = append(events, event) + } + return nil + }, + query, values..., + ) + if err != nil { + logging.New().WithError(err).Info("query failed") + return nil, errs.ThrowInternal(err, "SQL-IJuyR", "unable to filter events") } - return events, nil } func (db *SQL) LatestSequence(ctx context.Context, queryFactory *es_models.SearchQueryFactory) (uint64, error) { query, _, values, rowScanner := db.buildQuery(ctx, db.client, queryFactory) if query == "" { - return 0, errors.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") + return 0, errs.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") } - row := db.client.QueryRow(query, values...) sequence := new(Sequence) - err := rowScanner(row.Scan, sequence) - if err != nil { + err := db.client.QueryRowContext(ctx, func(row *sql.Row) error { + return rowScanner(row.Scan, sequence) + }, query, values...) + if err != nil && !errors.Is(err, sql.ErrNoRows) { logging.New().WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Info("query failed") - return 0, errors.ThrowInternal(err, "SQL-Yczyx", "unable to filter latest sequence") + return 0, errs.ThrowInternal(err, "SQL-Yczyx", "unable to filter latest sequence") } return uint64(*sequence), nil } -func (db *SQL) InstanceIDs(ctx context.Context, queryFactory *es_models.SearchQueryFactory) ([]string, error) { +func (db *SQL) InstanceIDs(ctx context.Context, queryFactory *es_models.SearchQueryFactory) (ids []string, err error) { query, _, values, rowScanner := db.buildQuery(ctx, db.client, queryFactory) if query == "" { - return nil, errors.ThrowInvalidArgument(nil, "SQL-Sfwg2", "invalid query factory") + return nil, errs.ThrowInvalidArgument(nil, "SQL-Sfwg2", "invalid query factory") } - rows, err := db.client.Query(query, values...) + err = db.client.QueryContext(ctx, + func(rows *sql.Rows) error { + for rows.Next() { + var id string + err := rowScanner(rows.Scan, &id) + if err != nil { + return err + } + + ids = append(ids, id) + } + return nil + }, + query, values...) if err != nil { logging.New().WithError(err).Info("query failed") - return nil, errors.ThrowInternal(err, "SQL-Sfg3r", "unable to filter instance ids") - } - defer rows.Close() - - ids := make([]string, 0) - - for rows.Next() { - var id string - err := rowScanner(rows.Scan, &id) - if err != nil { - return nil, err - } - - ids = append(ids, id) + return nil, errs.ThrowInternal(err, "SQL-Sfg3r", "unable to filter instance ids") } return ids, nil diff --git a/internal/eventstore/v1/internal/repository/sql/filter_test.go b/internal/eventstore/v1/internal/repository/sql/filter_test.go index 75863fed8..39ebbd0ad 100644 --- a/internal/eventstore/v1/internal/repository/sql/filter_test.go +++ b/internal/eventstore/v1/internal/repository/sql/filter_test.go @@ -130,6 +130,7 @@ func TestSQL_Filter(t *testing.T) { if (err != nil) != tt.res.wantErr { t.Errorf("SQL.Filter() error = %v, wantErr %v", err, tt.res.wantErr) } + if tt.res.eventsLen != 0 && len(events) != tt.res.eventsLen { t.Errorf("events has wrong length got: %d want %d", len(events), tt.res.eventsLen) } @@ -221,10 +222,12 @@ func TestSQL_LatestSequence(t *testing.T) { sql := &SQL{ client: &database.DB{DB: tt.fields.client.sqlClient, Database: new(testDB)}, } + sequence, err := sql.LatestSequence(context.Background(), tt.args.searchQuery) if (err != nil) != tt.res.wantErr { t.Errorf("SQL.Filter() error = %v, wantErr %v", err, tt.res.wantErr) } + if tt.res.sequence != sequence { t.Errorf("events has wrong length got: %d want %d", sequence, tt.res.sequence) } diff --git a/internal/logstore/emitters/access/database.go b/internal/logstore/emitters/access/database.go index bc121612e..de3afe5ae 100644 --- a/internal/logstore/emitters/access/database.go +++ b/internal/logstore/emitters/access/database.go @@ -2,6 +2,7 @@ package access import ( "context" + "database/sql" "fmt" "net/http" "strings" @@ -136,9 +137,15 @@ func (l *databaseLogStorage) QueryUsage(ctx context.Context, instanceId string, } var count uint64 - if err = l.dbClient. - QueryRowContext(ctx, stmt, args...). - Scan(&count); err != nil { + err = l.dbClient. + QueryRowContext(ctx, + func(row *sql.Row) error { + return row.Scan(&count) + }, + stmt, args..., + ) + + if err != nil { return 0, caos_errors.ThrowInternal(err, "ACCESS-pBPrM", "Errors.Logstore.Access.ScanFailed") } diff --git a/internal/logstore/emitters/execution/database.go b/internal/logstore/emitters/execution/database.go index 106b43320..396f3f2ea 100644 --- a/internal/logstore/emitters/execution/database.go +++ b/internal/logstore/emitters/execution/database.go @@ -2,6 +2,7 @@ package execution import ( "context" + "database/sql" "fmt" "time" @@ -113,9 +114,14 @@ func (l *databaseLogStorage) QueryUsage(ctx context.Context, instanceId string, } var durationSeconds uint64 - if err = l.dbClient. - QueryRowContext(ctx, stmt, args...). - Scan(&durationSeconds); err != nil { + err = l.dbClient. + QueryRowContext(ctx, + func(row *sql.Row) error { + return row.Scan(&durationSeconds) + }, + stmt, args..., + ) + if err != nil { return 0, caos_errors.ThrowInternal(err, "EXEC-Ad8nP", "Errors.Logstore.Execution.ScanFailed") } return durationSeconds, nil diff --git a/internal/query/action.go b/internal/query/action.go index 5c5a786bb..26aee576e 100644 --- a/internal/query/action.go +++ b/internal/query/action.go @@ -130,19 +130,19 @@ func (q *Queries) SearchActions(ctx context.Context, queries *ActionSearchQuerie return nil, errors.ThrowInvalidArgument(err, "QUERY-SDgwg", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + actions, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-SDfr52", "Errors.Internal") } - actions, err = scan(rows) - if err != nil { - return nil, err - } + actions.LatestSequence, err = q.latestSequence(ctx, actionTable) return actions, err } -func (q *Queries) GetActionByID(ctx context.Context, id string, orgID string, withOwnerRemoved bool) (_ *Action, err error) { +func (q *Queries) GetActionByID(ctx context.Context, id string, orgID string, withOwnerRemoved bool) (action *Action, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -160,8 +160,11 @@ func (q *Queries) GetActionByID(ctx context.Context, id string, orgID string, wi return nil, errors.ThrowInternal(err, "QUERY-Dgff3", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + action, err = scan(row) + return err + }, query, args...) + return action, err } func NewActionResourceOwnerQuery(id string) (SearchQuery, error) { diff --git a/internal/query/action_flow.go b/internal/query/action_flow.go index ca6c99923..0b263041a 100644 --- a/internal/query/action_flow.go +++ b/internal/query/action_flow.go @@ -67,7 +67,7 @@ type Flow struct { TriggerActions map[domain.TriggerType][]*Action } -func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType, orgID string, withOwnerRemoved bool) (_ *Flow, err error) { +func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType, orgID string, withOwnerRemoved bool) (flow *Flow, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -85,14 +85,14 @@ func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType, orgID s return nil, errors.ThrowInvalidArgument(err, "QUERY-HBRh3", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-Gg42f", "Errors.Internal") - } - return scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + flow, err = scan(rows) + return err + }, stmt, args...) + return flow, err } -func (q *Queries) GetActiveActionsByFlowAndTriggerType(ctx context.Context, flowType domain.FlowType, triggerType domain.TriggerType, orgID string, withOwnerRemoved bool) (_ []*Action, err error) { +func (q *Queries) GetActiveActionsByFlowAndTriggerType(ctx context.Context, flowType domain.FlowType, triggerType domain.TriggerType, orgID string, withOwnerRemoved bool) (actions []*Action, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -112,14 +112,14 @@ func (q *Queries) GetActiveActionsByFlowAndTriggerType(ctx context.Context, flow return nil, errors.ThrowInternal(err, "QUERY-Dgff3", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, query, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-SDf52", "Errors.Internal") - } - return scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + actions, err = scan(rows) + return err + }, query, args...) + return actions, err } -func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string, withOwnerRemoved bool) (_ []domain.FlowType, err error) { +func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string, withOwnerRemoved bool) (types []domain.FlowType, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -136,12 +136,11 @@ func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string, w return nil, errors.ThrowInvalidArgument(err, "QUERY-Dh311", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, query, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-Bhj4w", "Errors.Internal") - } - - return scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + types, err = scan(rows) + return err + }, query, args...) + return types, err } func prepareFlowTypesQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) ([]domain.FlowType, error)) { diff --git a/internal/query/action_flow_test.go b/internal/query/action_flow_test.go index a4a9131d8..897bbd04f 100644 --- a/internal/query/action_flow_test.go +++ b/internal/query/action_flow_test.go @@ -33,8 +33,8 @@ var ( ` projections.flow_triggers2.sequence,` + ` projections.flow_triggers2.resource_owner` + ` FROM projections.flow_triggers2` + - ` LEFT JOIN projections.actions3 ON projections.flow_triggers2.action_id = projections.actions3.id AND projections.flow_triggers2.instance_id = projections.actions3.instance_id` + - ` AS OF SYSTEM TIME '-1 ms'` + ` LEFT JOIN projections.actions3 ON projections.flow_triggers2.action_id = projections.actions3.id AND projections.flow_triggers2.instance_id = projections.actions3.instance_id` + // ` AS OF SYSTEM TIME '-1 ms'` prepareFlowCols = []string{ "id", "creation_date", @@ -66,8 +66,8 @@ var ( ` projections.actions3.allowed_to_fail,` + ` projections.actions3.timeout` + ` FROM projections.flow_triggers2` + - ` LEFT JOIN projections.actions3 ON projections.flow_triggers2.action_id = projections.actions3.id AND projections.flow_triggers2.instance_id = projections.actions3.instance_id` + - ` AS OF SYSTEM TIME '-1 ms'` + ` LEFT JOIN projections.actions3 ON projections.flow_triggers2.action_id = projections.actions3.id AND projections.flow_triggers2.instance_id = projections.actions3.instance_id` + // ` AS OF SYSTEM TIME '-1 ms'` prepareTriggerActionCols = []string{ "id", @@ -83,8 +83,8 @@ var ( } prepareFlowTypeStmt = `SELECT projections.flow_triggers2.flow_type` + - ` FROM projections.flow_triggers2` + - ` AS OF SYSTEM TIME '-1 ms'` + ` FROM projections.flow_triggers2` + // ` AS OF SYSTEM TIME '-1 ms'` prepareFlowTypeCols = []string{ "flow_type", diff --git a/internal/query/action_test.go b/internal/query/action_test.go index 2dfe6e8e3..4c0f00d07 100644 --- a/internal/query/action_test.go +++ b/internal/query/action_test.go @@ -25,8 +25,8 @@ var ( ` projections.actions3.timeout,` + ` projections.actions3.allowed_to_fail,` + ` COUNT(*) OVER ()` + - ` FROM projections.actions3` + - ` AS OF SYSTEM TIME '-1 ms'` + ` FROM projections.actions3` + // ` AS OF SYSTEM TIME '-1 ms'` prepareActionsCols = []string{ "id", "creation_date", @@ -51,8 +51,8 @@ var ( ` projections.actions3.script,` + ` projections.actions3.timeout,` + ` projections.actions3.allowed_to_fail` + - ` FROM projections.actions3` + - ` AS OF SYSTEM TIME '-1 ms'` + ` FROM projections.actions3` + // ` AS OF SYSTEM TIME '-1 ms'` prepareActionCols = []string{ "id", "creation_date", @@ -215,13 +215,13 @@ func Test_ActionPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Action)(nil), }, { name: "prepareActionQuery no result", prepare: prepareActionQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareActionStmt), nil, nil, @@ -284,7 +284,7 @@ func Test_ActionPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Action)(nil), }, } for _, tt := range tests { diff --git a/internal/query/app.go b/internal/query/app.go index 577f6c181..639b2e141 100644 --- a/internal/query/app.go +++ b/internal/query/app.go @@ -248,7 +248,7 @@ var ( } ) -func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string, withOwnerRemoved bool) (_ *App, err error) { +func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string, withOwnerRemoved bool) (app *App, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -270,11 +270,14 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo return nil, errors.ThrowInternal(err, "QUERY-AFDgg", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + app, err = scan(row) + return err + }, query, args...) + return app, err } -func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bool) (_ *App, err error) { +func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bool) (app *App, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -291,11 +294,14 @@ func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bo return nil, errors.ThrowInternal(err, "QUERY-immt9", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + app, err = scan(row) + return err + }, query, args...) + return app, err } -func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string, withOwnerRemoved bool) (_ *App, err error) { +func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string, withOwnerRemoved bool) (app *App, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -312,11 +318,14 @@ func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string, withOw return nil, errors.ThrowInternal(err, "QUERY-JgUop", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + app, err = scan(row) + return err + }, query, args...) + return app, err } -func (q *Queries) ProjectByClientID(ctx context.Context, appID string, withOwnerRemoved bool) (_ *Project, err error) { +func (q *Queries) ProjectByClientID(ctx context.Context, appID string, withOwnerRemoved bool) (project *Project, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -337,11 +346,14 @@ func (q *Queries) ProjectByClientID(ctx context.Context, appID string, withOwner return nil, errors.ThrowInternal(err, "QUERY-XhJi3", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + project, err = scan(row) + return err + }, query, args...) + return project, err } -func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, withOwnerRemoved bool) (_ string, err error) { +func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, withOwnerRemoved bool) (id string, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -358,11 +370,14 @@ func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, w return "", errors.ThrowInternal(err, "QUERY-7d92U", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + id, err = scan(row) + return err + }, query, args...) + return id, err } -func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string, withOwnerRemoved bool) (_ string, err error) { +func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string, withOwnerRemoved bool) (id string, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -384,11 +399,14 @@ func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string, withO return "", errors.ThrowInternal(err, "QUERY-SDfg3", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + id, err = scan(row) + return err + }, query, args...) + return id, err } -func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwnerRemoved bool) (_ *Project, err error) { +func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwnerRemoved bool) (project *Project, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -405,11 +423,14 @@ func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwne return nil, errors.ThrowInternal(err, "QUERY-XhJi4", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + project, err = scan(row) + return err + }, query, args...) + return project, err } -func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (_ *App, err error) { +func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (app *App, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -426,11 +447,14 @@ func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOw return nil, errors.ThrowInternal(err, "QUERY-JgVop", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + app, err = scan(row) + return err + }, query, args...) + return app, err } -func (q *Queries) AppByClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (_ *App, err error) { +func (q *Queries) AppByClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (app *App, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -450,11 +474,14 @@ func (q *Queries) AppByClientID(ctx context.Context, clientID string, withOwnerR return nil, errors.ThrowInternal(err, "QUERY-Dfge2", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + app, err = scan(row) + return err + }, query, args...) + return app, err } -func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, withOwnerRemoved bool) (_ *Apps, err error) { +func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, withOwnerRemoved bool) (apps *Apps, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -468,19 +495,18 @@ func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, wit return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + apps, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal") } - apps, err := scan(rows) - if err != nil { - return nil, err - } apps.LatestSequence, err = q.latestSequence(ctx, appsTable) return apps, err } -func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries, withOwnerRemoved bool) (_ []string, err error) { +func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries, withOwnerRemoved bool) (ids []string, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -494,11 +520,14 @@ func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + ids, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal") } - return scan(rows) + return ids, nil } func NewAppNameSearchQuery(method TextComparison, value string) (SearchQuery, error) { diff --git a/internal/query/app_test.go b/internal/query/app_test.go index e75e4ce06..ca93baddf 100644 --- a/internal/query/app_test.go +++ b/internal/query/app_test.go @@ -1115,7 +1115,7 @@ func Test_AppsPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*App)(nil), }, } for _, tt := range tests { @@ -1140,7 +1140,7 @@ func Test_AppPrepare(t *testing.T) { name: "prepareAppQuery no result", prepare: prepareAppQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( expectedAppQuery, nil, nil, @@ -1747,7 +1747,7 @@ func Test_AppPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*App)(nil), }, } for _, tt := range tests { @@ -1833,7 +1833,7 @@ func Test_AppIDsPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*App)(nil), }, } for _, tt := range tests { @@ -1858,7 +1858,7 @@ func Test_ProjectIDByAppPrepare(t *testing.T) { name: "prepareProjectIDByAppQuery no result", prepare: prepareProjectIDByAppQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( expectedProjectIDByAppQuery, nil, nil, @@ -1899,7 +1899,7 @@ func Test_ProjectIDByAppPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: "", }, } for _, tt := range tests { @@ -1924,7 +1924,7 @@ func Test_ProjectByAppPrepare(t *testing.T) { name: "prepareProjectByAppQuery no result", prepare: prepareProjectByAppQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( expectedProjectByAppQuery, nil, nil, @@ -2097,7 +2097,7 @@ func Test_ProjectByAppPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Project)(nil), }, } for _, tt := range tests { diff --git a/internal/query/auth_request.go b/internal/query/auth_request.go index ee91bdebe..334c101c9 100644 --- a/internal/query/auth_request.go +++ b/internal/query/auth_request.go @@ -60,12 +60,16 @@ func (q *Queries) AuthRequestByID(ctx context.Context, shouldTriggerBulk bool, i ) dst := new(AuthRequest) - err = q.client.DB.QueryRowContext( - ctx, q.authRequestByIDQuery(ctx), + err = q.client.QueryRowContext( + ctx, + func(row *sql.Row) error { + return row.Scan( + &dst.ID, &dst.CreationDate, &dst.LoginClient, &dst.ClientID, &scope, &dst.RedirectURI, + &prompt, &locales, &dst.LoginHint, &dst.MaxAge, &dst.HintUserID, + ) + }, + q.authRequestByIDQuery(ctx), id, authz.GetInstance(ctx).InstanceID(), - ).Scan( - &dst.ID, &dst.CreationDate, &dst.LoginClient, &dst.ClientID, &scope, &dst.RedirectURI, - &prompt, &locales, &dst.LoginHint, &dst.MaxAge, &dst.HintUserID, ) if errs.Is(err, sql.ErrNoRows) { return nil, errors.ThrowNotFound(err, "QUERY-Thee9", "Errors.AuthRequest.NotExisting") diff --git a/internal/query/auth_request_test.go b/internal/query/auth_request_test.go index 7348855e7..ba23b7315 100644 --- a/internal/query/auth_request_test.go +++ b/internal/query/auth_request_test.go @@ -125,7 +125,7 @@ func TestQueries_AuthRequestByID(t *testing.T) { shouldTriggerBulk: false, id: "123", }, - expect: mockQuery(expQuery, cols, nil, "123", "instanceID"), + expect: mockQueryScanErr(expQuery, cols, nil, "123", "instanceID"), wantErr: errors.ThrowNotFound(sql.ErrNoRows, "QUERY-Thee9", "Errors.AuthRequest.NotExisting"), }, { diff --git a/internal/query/authn_key.go b/internal/query/authn_key.go index 87140b216..2aa3576ad 100644 --- a/internal/query/authn_key.go +++ b/internal/query/authn_key.go @@ -144,14 +144,14 @@ func (q *Queries) SearchAuthNKeys(ctx context.Context, queries *AuthNKeySearchQu return nil, errors.ThrowInvalidArgument(err, "QUERY-SAf3f", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + authNKeys, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Dbg53", "Errors.Internal") } - authNKeys, err = scan(rows) - if err != nil { - return nil, err - } + authNKeys.LatestSequence, err = q.latestSequence(ctx, authNKeyTable) return authNKeys, err } @@ -174,19 +174,18 @@ func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySear return nil, errors.ThrowInvalidArgument(err, "QUERY-SAg3f", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + authNKeys, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Dbi53", "Errors.Internal") } - authNKeys, err = scan(rows) - if err != nil { - return nil, err - } authNKeys.LatestSequence, err = q.latestSequence(ctx, authNKeyTable) return authNKeys, err } -func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (_ *AuthNKey, err error) { +func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (key *AuthNKey, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -211,11 +210,14 @@ func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, i return nil, errors.ThrowInternal(err, "QUERY-AGhg4", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + key, err = scan(row) + return err + }, stmt, args...) + return key, err } -func (q *Queries) GetAuthNKeyPublicKeyByIDAndIdentifier(ctx context.Context, id string, identifier string, withOwnerRemoved bool) (_ []byte, err error) { +func (q *Queries) GetAuthNKeyPublicKeyByIDAndIdentifier(ctx context.Context, id string, identifier string, withOwnerRemoved bool) (key []byte, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -250,8 +252,11 @@ func (q *Queries) GetAuthNKeyPublicKeyByIDAndIdentifier(ctx context.Context, id return nil, errors.ThrowInternal(err, "QUERY-DAb32", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + key, err = scan(row) + return err + }, query, args...) + return key, err } func NewAuthNKeyResourceOwnerQuery(id string) (SearchQuery, error) { diff --git a/internal/query/authn_key_test.go b/internal/query/authn_key_test.go index 9deb9bfa4..620e6a507 100644 --- a/internal/query/authn_key_test.go +++ b/internal/query/authn_key_test.go @@ -349,13 +349,13 @@ func Test_AuthNKeyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*AuthNKey)(nil), }, { name: "prepareAuthNKeyQuery no result", prepare: prepareAuthNKeyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareAuthNKeyStmt), nil, nil, @@ -412,13 +412,13 @@ func Test_AuthNKeyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*AuthNKey)(nil), }, { name: "prepareAuthNKeyPublicKeyQuery no result", prepare: prepareAuthNKeyPublicKeyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareAuthNKeyPublicKeyStmt), nil, nil, @@ -461,7 +461,7 @@ func Test_AuthNKeyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: ([]byte)(nil), }, } for _, tt := range tests { diff --git a/internal/query/certificate.go b/internal/query/certificate.go index 285613880..6b5f06ebf 100644 --- a/internal/query/certificate.go +++ b/internal/query/certificate.go @@ -66,7 +66,7 @@ var ( } ) -func (q *Queries) ActiveCertificates(ctx context.Context, t time.Time, usage domain.KeyUsage) (_ *Certificates, err error) { +func (q *Queries) ActiveCertificates(ctx context.Context, t time.Time, usage domain.KeyUsage) (certs *Certificates, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -88,19 +88,19 @@ func (q *Queries) ActiveCertificates(ctx context.Context, t time.Time, usage dom return nil, errors.ThrowInternal(err, "QUERY-SDfkg", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + certs, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Sgan4", "Errors.Internal") } - keys, err := scan(rows) - if err != nil { - return nil, err - } - keys.LatestSequence, err = q.latestSequence(ctx, keyTable) + + certs.LatestSequence, err = q.latestSequence(ctx, keyTable) if !errors.IsNotFound(err) { - return keys, err + return certs, err } - return keys, nil + return certs, nil } func prepareCertificateQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*Certificates, error)) { diff --git a/internal/query/certificate_test.go b/internal/query/certificate_test.go index 4bd1de2be..6e8b52abf 100644 --- a/internal/query/certificate_test.go +++ b/internal/query/certificate_test.go @@ -138,7 +138,7 @@ func Test_CertificatePrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Certificate)(nil), }, } for _, tt := range tests { diff --git a/internal/query/current_sequence.go b/internal/query/current_sequence.go index ba7f27871..50c18a900 100644 --- a/internal/query/current_sequence.go +++ b/internal/query/current_sequence.go @@ -66,14 +66,17 @@ func (q *Queries) SearchCurrentSequences(ctx context.Context, queries *CurrentSe return nil, errors.ThrowInvalidArgument(err, "QUERY-MmFef", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + failedEvents, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-22H8f", "Errors.Internal") } - return scan(rows) + return failedEvents, nil } -func (q *Queries) latestSequence(ctx context.Context, projections ...table) (_ *LatestSequence, err error) { +func (q *Queries) latestSequence(ctx context.Context, projections ...table) (seq *LatestSequence, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -91,8 +94,12 @@ func (q *Queries) latestSequence(ctx context.Context, projections ...table) (_ * return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + seq, err = scan(row) + return err + }, stmt, args...) + + return seq, err } func (q *Queries) ClearCurrentSequence(ctx context.Context, projectionName string) (err error) { @@ -129,11 +136,17 @@ func (q *Queries) checkAndLock(ctx context.Context, projectionName string) error if err != nil { return errors.ThrowInternal(err, "QUERY-Dfwf2", "Errors.ProjectionName.Invalid") } - row := q.client.QueryRowContext(ctx, projectionQuery, args...) var count int - if err := row.Scan(&count); err != nil || count == 0 { - return errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + if err := row.Scan(&count); err != nil || count == 0 { + return errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") + } + return err + }, projectionQuery, args...) + if err != nil { + return err } + lock := fmt.Sprintf(lockStmtFormat, locksTable.identifier()) if err != nil { return errors.ThrowInternal(err, "QUERY-DVfg3", "Errors.RemoveFailed") diff --git a/internal/query/current_sequence_test.go b/internal/query/current_sequence_test.go index cdd78a43c..20df6aea0 100644 --- a/internal/query/current_sequence_test.go +++ b/internal/query/current_sequence_test.go @@ -132,7 +132,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*CurrentSequences)(nil), }, } for _, tt := range tests { diff --git a/internal/query/custom_text.go b/internal/query/custom_text.go index 415cbe302..5f8386680 100644 --- a/internal/query/custom_text.go +++ b/internal/query/custom_text.go @@ -104,14 +104,14 @@ func (q *Queries) CustomTextList(ctx context.Context, aggregateID, template, lan return nil, errors.ThrowInternal(err, "QUERY-M9gse", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, query, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + texts, err = scan(rows) + return err + }, query, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-2j00f", "Errors.Internal") } - texts, err = scan(rows) - if err != nil { - return nil, err - } + texts.LatestSequence, err = q.latestSequence(ctx, projectsTable) return texts, err } @@ -134,14 +134,14 @@ func (q *Queries) CustomTextListByTemplate(ctx context.Context, aggregateID, tem return nil, errors.ThrowInternal(err, "QUERY-M49fs", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, query, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + texts, err = scan(rows) + return err + }, query, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-3n9ge", "Errors.Internal") } - texts, err = scan(rows) - if err != nil { - return nil, err - } + texts.LatestSequence, err = q.latestSequence(ctx, projectsTable) return texts, err } diff --git a/internal/query/custom_text_test.go b/internal/query/custom_text_test.go index 93a9508ba..3df40638c 100644 --- a/internal/query/custom_text_test.go +++ b/internal/query/custom_text_test.go @@ -180,7 +180,7 @@ func Test_CustomTextPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*CustomText)(nil), }, } for _, tt := range tests { diff --git a/internal/query/device_auth.go b/internal/query/device_auth.go index 98faff200..91d26ba6c 100644 --- a/internal/query/device_auth.go +++ b/internal/query/device_auth.go @@ -70,7 +70,7 @@ var ( } ) -func (q *Queries) DeviceAuthByDeviceCode(ctx context.Context, clientID, deviceCode string) (_ *domain.DeviceAuth, err error) { +func (q *Queries) DeviceAuthByDeviceCode(ctx context.Context, clientID, deviceCode string) (deviceAuth *domain.DeviceAuth, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -85,10 +85,14 @@ func (q *Queries) DeviceAuthByDeviceCode(ctx context.Context, clientID, deviceCo return nil, errors.ThrowInternal(err, "QUERY-uk1Oh", "Errors.Query.SQLStatement") } - return scan(q.client.QueryRowContext(ctx, query, args...)) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + deviceAuth, err = scan(row) + return err + }, query, args...) + return deviceAuth, err } -func (q *Queries) DeviceAuthByUserCode(ctx context.Context, userCode string) (_ *domain.DeviceAuth, err error) { +func (q *Queries) DeviceAuthByUserCode(ctx context.Context, userCode string) (deviceAuth *domain.DeviceAuth, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -102,7 +106,11 @@ func (q *Queries) DeviceAuthByUserCode(ctx context.Context, userCode string) (_ return nil, errors.ThrowInternal(err, "QUERY-Axu7l", "Errors.Query.SQLStatement") } - return scan(q.client.QueryRowContext(ctx, query, args...)) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + deviceAuth, err = scan(row) + return err + }, query, args...) + return deviceAuth, err } var deviceAuthSelectColumns = []string{ diff --git a/internal/query/device_auth_test.go b/internal/query/device_auth_test.go index 938cb9f84..112032d32 100644 --- a/internal/query/device_auth_test.go +++ b/internal/query/device_auth_test.go @@ -67,9 +67,11 @@ func TestQueries_DeviceAuthByDeviceCode(t *testing.T) { } defer client.Close() + mock.ExpectBegin() mock.ExpectQuery(expectedDeviceAuthWhereDeviceCodeQuery).WillReturnRows( sqlmock.NewRows(deviceAuthSelectColumns).AddRow(expectedDeviceAuthValues...), ) + mock.ExpectCommit() q := Queries{ client: &database.DB{DB: client}, } @@ -86,9 +88,11 @@ func TestQueries_DeviceAuthByUserCode(t *testing.T) { } defer client.Close() + mock.ExpectBegin() mock.ExpectQuery(expectedDeviceAuthWhereUserCodeQuery).WillReturnRows( sqlmock.NewRows(deviceAuthSelectColumns).AddRow(expectedDeviceAuthValues...), ) + mock.ExpectCommit() q := Queries{ client: &database.DB{DB: client}, } @@ -133,6 +137,7 @@ func Test_prepareDeviceAuthQuery(t *testing.T) { return nil, true }, }, + object: (*domain.DeviceAuth)(nil), }, { name: "other error", @@ -148,6 +153,7 @@ func Test_prepareDeviceAuthQuery(t *testing.T) { return nil, true }, }, + object: (*domain.DeviceAuth)(nil), }, } for _, tt := range tests { diff --git a/internal/query/domain_policy.go b/internal/query/domain_policy.go index 2f7a596cd..77e821d39 100644 --- a/internal/query/domain_policy.go +++ b/internal/query/domain_policy.go @@ -86,7 +86,7 @@ var ( } ) -func (q *Queries) DomainPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *DomainPolicy, err error) { +func (q *Queries) DomainPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *DomainPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -120,11 +120,14 @@ func (q *Queries) DomainPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, return nil, errors.ThrowInternal(err, "QUERY-D3CqT", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultDomainPolicy(ctx context.Context) (_ *DomainPolicy, err error) { +func (q *Queries) DefaultDomainPolicy(ctx context.Context) (policy *DomainPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -139,8 +142,11 @@ func (q *Queries) DefaultDomainPolicy(ctx context.Context) (_ *DomainPolicy, err return nil, errors.ThrowInternal(err, "QUERY-pM7lP", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func prepareDomainPolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*DomainPolicy, error)) { diff --git a/internal/query/domain_policy_test.go b/internal/query/domain_policy_test.go index 5940555b2..ef861c737 100644 --- a/internal/query/domain_policy_test.go +++ b/internal/query/domain_policy_test.go @@ -54,7 +54,7 @@ func Test_DomainPolicyPrepares(t *testing.T) { name: "prepareDomainPolicyQuery no result", prepare: prepareDomainPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareDomainPolicyStmt), nil, nil, @@ -117,7 +117,7 @@ func Test_DomainPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*DomainPolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/failed_events.go b/internal/query/failed_events.go index 2afd28d5e..ed335bf62 100644 --- a/internal/query/failed_events.go +++ b/internal/query/failed_events.go @@ -77,11 +77,14 @@ func (q *Queries) SearchFailedEvents(ctx context.Context, queries *FailedEventSe return nil, errors.ThrowInvalidArgument(err, "QUERY-n8rjJ", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + failedEvents, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-3j99J", "Errors.Internal") } - return scan(rows) + return failedEvents, nil } func (q *Queries) RemoveFailedEvent(ctx context.Context, projectionName, instanceID string, sequence uint64) (err error) { diff --git a/internal/query/failed_events_test.go b/internal/query/failed_events_test.go index ede972c27..01ba2bee2 100644 --- a/internal/query/failed_events_test.go +++ b/internal/query/failed_events_test.go @@ -146,7 +146,7 @@ func Test_FailedEventsPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*FailedEvents)(nil), }, } for _, tt := range tests { diff --git a/internal/query/iam_member.go b/internal/query/iam_member.go index ae8e601ab..bba45fbf5 100644 --- a/internal/query/iam_member.go +++ b/internal/query/iam_member.go @@ -76,7 +76,7 @@ func addIamMemberWithoutOwnerRemoved(eq map[string]interface{}) { eq[InstanceMemberOwnerRemovedUser.identifier()] = false } -func (q *Queries) IAMMembers(ctx context.Context, queries *IAMMembersQuery, withOwnerRemoved bool) (_ *Members, err error) { +func (q *Queries) IAMMembers(ctx context.Context, queries *IAMMembersQuery, withOwnerRemoved bool) (members *Members, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -96,14 +96,13 @@ func (q *Queries) IAMMembers(ctx context.Context, queries *IAMMembersQuery, with return nil, err } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + members, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Pdg1I", "Errors.Internal") } - members, err := scan(rows) - if err != nil { - return nil, err - } members.LatestSequence = currentSequence return members, err } diff --git a/internal/query/iam_member_test.go b/internal/query/iam_member_test.go index 849f420ce..ed2bd6cd3 100644 --- a/internal/query/iam_member_test.go +++ b/internal/query/iam_member_test.go @@ -280,7 +280,7 @@ func Test_IAMMemberPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IAMMembership)(nil), }, } for _, tt := range tests { diff --git a/internal/query/idp.go b/internal/query/idp.go index 35c0b7b5d..e29077b0b 100644 --- a/internal/query/idp.go +++ b/internal/query/idp.go @@ -188,7 +188,7 @@ var ( ) // IDPByIDAndResourceOwner searches for the requested id in the context of the resource owner and IAM -func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, shouldTriggerBulk bool, id, resourceOwner string, withOwnerRemoved bool) (_ *IDP, err error) { +func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, shouldTriggerBulk bool, id, resourceOwner string, withOwnerRemoved bool) (idp *IDP, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -216,8 +216,11 @@ func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, shouldTriggerBulk return nil, errors.ThrowInternal(err, "QUERY-0gocI", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + idp, err = scan(row) + return err + }, query, args...) + return idp, err } // IDPs searches idps matching the query @@ -237,14 +240,13 @@ func (q *Queries) IDPs(ctx context.Context, queries *IDPSearchQueries, withOwner return nil, errors.ThrowInvalidArgument(err, "QUERY-X6X7y", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + idps, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-xPlVH", "Errors.Internal") } - idps, err = scan(rows) - if err != nil { - return nil, err - } idps.LatestSequence, err = q.latestSequence(ctx, idpTable) return idps, err } diff --git a/internal/query/idp_login_policy_link.go b/internal/query/idp_login_policy_link.go index 750f2f114..89ee4a08b 100644 --- a/internal/query/idp_login_policy_link.go +++ b/internal/query/idp_login_policy_link.go @@ -105,13 +105,13 @@ func (q *Queries) IDPLoginPolicyLinks(ctx context.Context, resourceOwner string, if err != nil { return nil, errors.ThrowInvalidArgument(err, "QUERY-FDbKW", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil || rows.Err() != nil { - return nil, errors.ThrowInternal(err, "QUERY-ZkKUc", "Errors.Internal") - } - idps, err = scan(rows) + + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + idps, err = scan(rows) + return err + }, stmt, args...) if err != nil { - return nil, err + return nil, errors.ThrowInternal(err, "QUERY-ZkKUc", "Errors.Internal") } idps.LatestSequence, err = q.latestSequence(ctx, idpLoginPolicyLinkTable) return idps, err diff --git a/internal/query/idp_login_policy_link_test.go b/internal/query/idp_login_policy_link_test.go index 22f07e7fa..4f720b86a 100644 --- a/internal/query/idp_login_policy_link_test.go +++ b/internal/query/idp_login_policy_link_test.go @@ -128,7 +128,7 @@ func Test_IDPLoginPolicyLinkPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDPs)(nil), }, } for _, tt := range tests { diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index b83efe156..a19d6b512 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -606,7 +606,7 @@ var ( ) // IDPTemplateByID searches for the requested id -func (q *Queries) IDPTemplateByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (_ *IDPTemplate, err error) { +func (q *Queries) IDPTemplateByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (template *IDPTemplate, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -630,8 +630,11 @@ func (q *Queries) IDPTemplateByID(ctx context.Context, shouldTriggerBulk bool, i return nil, errors.ThrowInternal(err, "QUERY-SFefg", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + template, err = scan(row) + return err + }, stmt, args...) + return template, err } // IDPTemplates searches idp templates matching the query @@ -651,14 +654,13 @@ func (q *Queries) IDPTemplates(ctx context.Context, queries *IDPTemplateSearchQu return nil, errors.ThrowInvalidArgument(err, "QUERY-SAF34", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + idps, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-BDFrq", "Errors.Internal") } - idps, err = scan(rows) - if err != nil { - return nil, err - } idps.LatestSequence, err = q.latestSequence(ctx, idpTemplateTable) return idps, err } diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index a646861b7..34c3ad372 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -443,7 +443,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { name: "prepareIDPTemplateByIDQuery no result", prepare: prepareIDPTemplateByIDQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(idpTemplateQuery), nil, nil, @@ -1646,7 +1646,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDPTemplate)(nil), }, { name: "prepareIDPTemplatesQuery no result", @@ -2606,7 +2606,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDPTemplates)(nil), }, } for _, tt := range tests { diff --git a/internal/query/idp_test.go b/internal/query/idp_test.go index 6a692ed16..a44252369 100644 --- a/internal/query/idp_test.go +++ b/internal/query/idp_test.go @@ -144,7 +144,7 @@ func Test_IDPPrepares(t *testing.T) { name: "prepareIDPByIDQuery no result", prepare: prepareIDPByIDQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(idpQuery), nil, nil, @@ -341,7 +341,7 @@ func Test_IDPPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDP)(nil), }, { name: "prepareIDPsQuery no result", @@ -728,7 +728,7 @@ func Test_IDPPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDPs)(nil), }, } for _, tt := range tests { diff --git a/internal/query/idp_user_link.go b/internal/query/idp_user_link.go index b2b95e85e..346ce129e 100644 --- a/internal/query/idp_user_link.go +++ b/internal/query/idp_user_link.go @@ -103,14 +103,13 @@ func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQ return nil, errors.ThrowInvalidArgument(err, "QUERY-4zzFK", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + idps, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-C1E4D", "Errors.Internal") } - idps, err = scan(rows) - if err != nil { - return nil, err - } idps.LatestSequence, err = q.latestSequence(ctx, idpUserLinkTable) return idps, err } diff --git a/internal/query/idp_user_link_test.go b/internal/query/idp_user_link_test.go index 652d905a5..af4e3a54d 100644 --- a/internal/query/idp_user_link_test.go +++ b/internal/query/idp_user_link_test.go @@ -135,7 +135,7 @@ func Test_IDPUserLinkPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*IDPUserLinks)(nil), }, } for _, tt := range tests { diff --git a/internal/query/instance.go b/internal/query/instance.go index 9040478af..77327e736 100644 --- a/internal/query/instance.go +++ b/internal/query/instance.go @@ -166,18 +166,17 @@ func (q *Queries) SearchInstances(ctx context.Context, queries *InstanceSearchQu return nil, errors.ThrowInvalidArgument(err, "QUERY-M9fow", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + instances, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-3j98f", "Errors.Internal") } - instances, err = scan(rows) - if err != nil { - return nil, err - } return instances, err } -func (q *Queries) Instance(ctx context.Context, shouldTriggerBulk bool) (_ *Instance, err error) { +func (q *Queries) Instance(ctx context.Context, shouldTriggerBulk bool) (instance *Instance, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -193,14 +192,14 @@ func (q *Queries) Instance(ctx context.Context, shouldTriggerBulk bool) (_ *Inst return nil, errors.ThrowInternal(err, "QUERY-d9ngs", "Errors.Query.SQLStatement") } - row, err := q.client.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - return scan(row) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + instance, err = scan(rows) + return err + }, query, args...) + return instance, err } -func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Instance, err error) { +func (q *Queries) InstanceByHost(ctx context.Context, host string) (instance authz.Instance, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -213,11 +212,11 @@ func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Inst return nil, errors.ThrowInternal(err, "QUERY-SAfg2", "Errors.Query.SQLStatement") } - row, err := q.client.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - return scan(row) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + instance, err = scan(rows) + return err + }, query, args...) + return instance, err } func (q *Queries) InstanceByID(ctx context.Context) (_ authz.Instance, err error) { diff --git a/internal/query/instance_domain.go b/internal/query/instance_domain.go index 6eedd3f06..bb11e1aa8 100644 --- a/internal/query/instance_domain.go +++ b/internal/query/instance_domain.go @@ -88,11 +88,10 @@ func (q *Queries) SearchInstanceDomainsGlobal(ctx context.Context, queries *Inst } func (q *Queries) queryInstanceDomains(ctx context.Context, stmt string, scan func(*sql.Rows) (*InstanceDomains, error), args ...interface{}) (domains *InstanceDomains, err error) { - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-Dh9Ap", "Errors.Internal") - } - domains, err = scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + domains, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } diff --git a/internal/query/instance_domain_test.go b/internal/query/instance_domain_test.go index 06d8e8dc0..4f72c0def 100644 --- a/internal/query/instance_domain_test.go +++ b/internal/query/instance_domain_test.go @@ -162,7 +162,7 @@ func Test_InstanceDomainPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Domains)(nil), }, } for _, tt := range tests { diff --git a/internal/query/instance_test.go b/internal/query/instance_test.go index 032063128..a14b44df0 100644 --- a/internal/query/instance_test.go +++ b/internal/query/instance_test.go @@ -97,7 +97,7 @@ func Test_InstancePrepares(t *testing.T) { additionalArgs: []reflect.Value{reflect.ValueOf("")}, prepare: prepareInstanceQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(instanceQuery), nil, nil, @@ -160,7 +160,7 @@ func Test_InstancePrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Instance)(nil), }, { name: "prepareInstancesQuery no result", diff --git a/internal/query/key.go b/internal/query/key.go index 1c9891383..29a39fa06 100644 --- a/internal/query/key.go +++ b/internal/query/key.go @@ -177,7 +177,7 @@ var ( } ) -func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (_ *PublicKeys, err error) { +func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (keys *PublicKeys, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -194,14 +194,14 @@ func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (_ *PublicK return nil, errors.ThrowInternal(err, "QUERY-SDFfg", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + keys, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Sghn4", "Errors.Internal") } - keys, err := scan(rows) - if err != nil { - return nil, err - } + keys.LatestSequence, err = q.latestSequence(ctx, keyTable) if !errors.IsNotFound(err) { return keys, err @@ -209,7 +209,7 @@ func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (_ *PublicK return keys, nil } -func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (_ *PrivateKeys, err error) { +func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (keys *PrivateKeys, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -229,14 +229,13 @@ func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (_ * return nil, errors.ThrowInternal(err, "QUERY-SDff2", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, query, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + keys, err = scan(rows) + return err + }, query, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-WRFG4", "Errors.Internal") } - keys, err := scan(rows) - if err != nil { - return nil, err - } keys.LatestSequence, err = q.latestSequence(ctx, keyTable) if !errors.IsNotFound(err) { return keys, err diff --git a/internal/query/key_test.go b/internal/query/key_test.go index 26ab88971..a600b23ee 100644 --- a/internal/query/key_test.go +++ b/internal/query/key_test.go @@ -147,7 +147,7 @@ func Test_KeyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PublicKeys)(nil), }, { name: "preparePrivateKeysQuery no result", @@ -230,7 +230,7 @@ func Test_KeyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PrivateKeys)(nil), }, } for _, tt := range tests { diff --git a/internal/query/label_policy.go b/internal/query/label_policy.go index 731dbba2e..476c8de59 100644 --- a/internal/query/label_policy.go +++ b/internal/query/label_policy.go @@ -42,7 +42,7 @@ type Theme struct { IconURL string } -func (q *Queries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (_ *LabelPolicy, err error) { +func (q *Queries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (policy *LabelPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -68,11 +68,14 @@ func (q *Queries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string, with return nil, errors.ThrowInternal(err, "QUERY-V22un", "unable to create sql stmt") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (_ *LabelPolicy, err error) { +func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (policy *LabelPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -98,11 +101,14 @@ func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (_ return nil, errors.ThrowInternal(err, "QUERY-AG5eq", "unable to create sql stmt") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (_ *LabelPolicy, err error) { +func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (policy *LabelPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -118,11 +124,14 @@ func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (_ *LabelPolicy, return nil, errors.ThrowInternal(err, "QUERY-mN0Ci", "unable to create sql stmt") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultPreviewLabelPolicy(ctx context.Context) (_ *LabelPolicy, err error) { +func (q *Queries) DefaultPreviewLabelPolicy(ctx context.Context) (policy *LabelPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -138,8 +147,11 @@ func (q *Queries) DefaultPreviewLabelPolicy(ctx context.Context) (_ *LabelPolicy return nil, errors.ThrowInternal(err, "QUERY-B3JQR", "unable to create sql stmt") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } var ( diff --git a/internal/query/lockout_policy.go b/internal/query/lockout_policy.go index ff9dfc663..7e7624dc2 100644 --- a/internal/query/lockout_policy.go +++ b/internal/query/lockout_policy.go @@ -81,7 +81,7 @@ var ( } ) -func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *LockoutPolicy, err error) { +func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *LockoutPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -110,11 +110,14 @@ func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool return nil, errors.ThrowInternal(err, "QUERY-SKR6X", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultLockoutPolicy(ctx context.Context) (_ *LockoutPolicy, err error) { +func (q *Queries) DefaultLockoutPolicy(ctx context.Context) (policy *LockoutPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -129,8 +132,11 @@ func (q *Queries) DefaultLockoutPolicy(ctx context.Context) (_ *LockoutPolicy, e return nil, errors.ThrowInternal(err, "QUERY-mN0Ci", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func prepareLockoutPolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*LockoutPolicy, error)) { diff --git a/internal/query/lockout_policy_test.go b/internal/query/lockout_policy_test.go index 5c1833182..0fefcf386 100644 --- a/internal/query/lockout_policy_test.go +++ b/internal/query/lockout_policy_test.go @@ -53,7 +53,7 @@ func Test_LockoutPolicyPrepares(t *testing.T) { name: "prepareLockoutPolicyQuery no result", prepare: prepareLockoutPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareLockoutPolicyStmt), nil, nil, @@ -114,7 +114,7 @@ func Test_LockoutPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*LockoutPolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/login_policy.go b/internal/query/login_policy.go index dcf168d94..5d0a7e668 100644 --- a/internal/query/login_policy.go +++ b/internal/query/login_policy.go @@ -166,7 +166,7 @@ var ( } ) -func (q *Queries) LoginPolicyByID(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *LoginPolicy, err error) { +func (q *Queries) LoginPolicyByID(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *LoginPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -191,11 +191,14 @@ func (q *Queries) LoginPolicyByID(ctx context.Context, shouldTriggerBulk bool, o return nil, errors.ThrowInternal(err, "QUERY-scVHo", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + policy, err = q.scanAndAddLinksToLoginPolicy(ctx, rows, scan) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-SWgr3", "Errors.Internal") } - return q.scanAndAddLinksToLoginPolicy(ctx, rows, scan) + return policy, nil } func (q *Queries) scanAndAddLinksToLoginPolicy(ctx context.Context, rows *sql.Rows, scan func(*sql.Rows) (*LoginPolicy, error)) (*LoginPolicy, error) { @@ -214,7 +217,7 @@ func (q *Queries) scanAndAddLinksToLoginPolicy(ctx context.Context, rows *sql.Ro return policy, nil } -func (q *Queries) DefaultLoginPolicy(ctx context.Context) (_ *LoginPolicy, err error) { +func (q *Queries) DefaultLoginPolicy(ctx context.Context) (policy *LoginPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -227,14 +230,17 @@ func (q *Queries) DefaultLoginPolicy(ctx context.Context) (_ *LoginPolicy, err e return nil, errors.ThrowInternal(err, "QUERY-t4TBK", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + policy, err = q.scanAndAddLinksToLoginPolicy(ctx, rows, scan) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-SArt2", "Errors.Internal") } - return q.scanAndAddLinksToLoginPolicy(ctx, rows, scan) + return policy, nil } -func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (_ *SecondFactors, err error) { +func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (factors *SecondFactors, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -259,8 +265,10 @@ func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (_ *Seco return nil, errors.ThrowInternal(err, "QUERY-scVHo", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - factors, err := scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + factors, err = scan(row) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -268,7 +276,7 @@ func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (_ *Seco return factors, err } -func (q *Queries) DefaultSecondFactors(ctx context.Context) (_ *SecondFactors, err error) { +func (q *Queries) DefaultSecondFactors(ctx context.Context) (factors *SecondFactors, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -281,8 +289,10 @@ func (q *Queries) DefaultSecondFactors(ctx context.Context) (_ *SecondFactors, e return nil, errors.ThrowInternal(err, "QUERY-CZ2Nv", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - factors, err := scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + factors, err = scan(row) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -290,7 +300,7 @@ func (q *Queries) DefaultSecondFactors(ctx context.Context) (_ *SecondFactors, e return factors, err } -func (q *Queries) MultiFactorsByOrg(ctx context.Context, orgID string) (_ *MultiFactors, err error) { +func (q *Queries) MultiFactorsByOrg(ctx context.Context, orgID string) (factors *MultiFactors, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -315,8 +325,10 @@ func (q *Queries) MultiFactorsByOrg(ctx context.Context, orgID string) (_ *Multi return nil, errors.ThrowInternal(err, "QUERY-B4o7h", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - factors, err := scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + factors, err = scan(row) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -324,7 +336,7 @@ func (q *Queries) MultiFactorsByOrg(ctx context.Context, orgID string) (_ *Multi return factors, err } -func (q *Queries) DefaultMultiFactors(ctx context.Context) (_ *MultiFactors, err error) { +func (q *Queries) DefaultMultiFactors(ctx context.Context) (factors *MultiFactors, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -337,8 +349,10 @@ func (q *Queries) DefaultMultiFactors(ctx context.Context) (_ *MultiFactors, err return nil, errors.ThrowInternal(err, "QUERY-WxYjr", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - factors, err := scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + factors, err = scan(row) + return err + }, stmt, args...) if err != nil { return nil, err } diff --git a/internal/query/login_policy_test.go b/internal/query/login_policy_test.go index 9aa398d81..6da7e3487 100644 --- a/internal/query/login_policy_test.go +++ b/internal/query/login_policy_test.go @@ -98,7 +98,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { name: "prepareLoginPolicyQuery no result", prepare: prepareLoginPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(loginPolicyQuery), nil, nil, @@ -189,13 +189,13 @@ func Test_LoginPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*LoginPolicy)(nil), }, { name: "prepareLoginPolicy2FAsQuery no result", prepare: prepareLoginPolicy2FAsQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(prepareLoginPolicy2FAsStmt), prepareLoginPolicy2FAsCols, nil, @@ -257,13 +257,13 @@ func Test_LoginPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SecondFactors)(nil), }, { name: "prepareLoginPolicyMFAsQuery no result", prepare: prepareLoginPolicyMFAsQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(prepareLoginPolicyMFAsStmt), prepareLoginPolicyMFAsCols, nil, @@ -325,7 +325,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*MultiFactors)(nil), }, } for _, tt := range tests { diff --git a/internal/query/mail_template.go b/internal/query/mail_template.go index 13459a4a9..f0872cf29 100644 --- a/internal/query/mail_template.go +++ b/internal/query/mail_template.go @@ -70,7 +70,7 @@ var ( } ) -func (q *Queries) MailTemplateByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (_ *MailTemplate, err error) { +func (q *Queries) MailTemplateByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (template *MailTemplate, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -93,11 +93,14 @@ func (q *Queries) MailTemplateByOrg(ctx context.Context, orgID string, withOwner return nil, errors.ThrowInternal(err, "QUERY-m0sJg", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + template, err = scan(row) + return err + }, query, args...) + return template, err } -func (q *Queries) DefaultMailTemplate(ctx context.Context) (_ *MailTemplate, err error) { +func (q *Queries) DefaultMailTemplate(ctx context.Context) (template *MailTemplate, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -112,8 +115,11 @@ func (q *Queries) DefaultMailTemplate(ctx context.Context) (_ *MailTemplate, err return nil, errors.ThrowInternal(err, "QUERY-2m0fH", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + template, err = scan(row) + return err + }, query, args...) + return template, err } func prepareMailTemplateQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*MailTemplate, error)) { diff --git a/internal/query/message_text.go b/internal/query/message_text.go index fc6b44569..4bc656c0c 100644 --- a/internal/query/message_text.go +++ b/internal/query/message_text.go @@ -126,7 +126,7 @@ var ( } ) -func (q *Queries) DefaultMessageText(ctx context.Context) (_ *MessageText, err error) { +func (q *Queries) DefaultMessageText(ctx context.Context) (text *MessageText, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -140,8 +140,11 @@ func (q *Queries) DefaultMessageText(ctx context.Context) (_ *MessageText, err e return nil, errors.ThrowInternal(err, "QUERY-1b9mf", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + text, err = scan(row) + return err + }, query, args...) + return text, err } func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx context.Context, messageType, language string) (_ *MessageText, err error) { @@ -159,7 +162,7 @@ func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx context. return messageTexts.GetMessageTextByType(messageType), nil } -func (q *Queries) CustomMessageTextByTypeAndLanguage(ctx context.Context, aggregateID, messageType, language string, withOwnerRemoved bool) (_ *MessageText, err error) { +func (q *Queries) CustomMessageTextByTypeAndLanguage(ctx context.Context, aggregateID, messageType, language string, withOwnerRemoved bool) (msg *MessageText, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -179,8 +182,10 @@ func (q *Queries) CustomMessageTextByTypeAndLanguage(ctx context.Context, aggreg return nil, errors.ThrowInternal(err, "QUERY-1b9mf", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - msg, err := scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + msg, err = scan(row) + return err + }, query, args...) if errors.IsNotFound(err) { return q.IAMMessageTextByTypeAndLanguage(ctx, messageType, language) } diff --git a/internal/query/message_text_test.go b/internal/query/message_text_test.go index d371a2f17..713066512 100644 --- a/internal/query/message_text_test.go +++ b/internal/query/message_text_test.go @@ -64,7 +64,7 @@ func Test_MessageTextPrepares(t *testing.T) { name: "prepareMessageTextQuery no result", prepare: prepareMessageTextQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareMessageTextStmt), nil, nil, @@ -135,7 +135,7 @@ func Test_MessageTextPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*MessageText)(nil), }, } for _, tt := range tests { diff --git a/internal/query/milestone.go b/internal/query/milestone.go index b4767495b..e781151d7 100644 --- a/internal/query/milestone.go +++ b/internal/query/milestone.go @@ -69,7 +69,7 @@ var ( ) // SearchMilestones tries to defer the instanceID from the passed context if no instanceIDs are passed -func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, queries *MilestonesSearchQueries) (_ *Milestones, err error) { +func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, queries *MilestonesSearchQueries) (milestones *Milestones, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() query, scan := prepareMilestonesQuery(ctx, q.client) @@ -80,22 +80,14 @@ func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, qu if err != nil { return nil, errors.ThrowInternal(err, "QUERY-A9i5k", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + milestones, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } - defer func() { - if closeErr := rows.Close(); closeErr != nil && err == nil { - err = errors.ThrowInternal(closeErr, "QUERY-CK9mI", "Errors.Query.CloseRows") - } - }() - milestones, err := scan(rows) - if err != nil { - return nil, err - } - if err = rows.Err(); err != nil { - return nil, errors.ThrowInternal(err, "QUERY-asLsI", "Errors.Internal") - } + milestones.LatestSequence, err = q.latestSequence(ctx, milestonesTable) return milestones, err diff --git a/internal/query/milestone_test.go b/internal/query/milestone_test.go index 1c1852a59..b0b7ec8b5 100644 --- a/internal/query/milestone_test.go +++ b/internal/query/milestone_test.go @@ -178,7 +178,7 @@ func Test_MilestonesPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Milestones)(nil), }, } for _, tt := range tests { diff --git a/internal/query/notification_policy.go b/internal/query/notification_policy.go index 64873e1ca..c015c1c96 100644 --- a/internal/query/notification_policy.go +++ b/internal/query/notification_policy.go @@ -76,7 +76,7 @@ var ( } ) -func (q *Queries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *NotificationPolicy, err error) { +func (q *Queries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *NotificationPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -104,11 +104,14 @@ func (q *Queries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk return nil, errors.ThrowInternal(err, "QUERY-Xuoapqm", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultNotificationPolicy(ctx context.Context, shouldTriggerBulk bool) (_ *NotificationPolicy, err error) { +func (q *Queries) DefaultNotificationPolicy(ctx context.Context, shouldTriggerBulk bool) (policy *NotificationPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -130,8 +133,11 @@ func (q *Queries) DefaultNotificationPolicy(ctx context.Context, shouldTriggerBu return nil, errors.ThrowInternal(err, "QUERY-xlqp209", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func prepareNotificationPolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*NotificationPolicy, error)) { diff --git a/internal/query/notification_policy_test.go b/internal/query/notification_policy_test.go index b318531fb..3c5de860f 100644 --- a/internal/query/notification_policy_test.go +++ b/internal/query/notification_policy_test.go @@ -50,7 +50,7 @@ func Test_NotificationPolicyPrepares(t *testing.T) { name: "prepareNotificationPolicyQuery no result", prepare: prepareNotificationPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( notificationPolicyStmt, nil, nil, @@ -109,7 +109,7 @@ func Test_NotificationPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*NotificationPolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/notification_provider.go b/internal/query/notification_provider.go index bbc0c50a0..7ba7320e2 100644 --- a/internal/query/notification_provider.go +++ b/internal/query/notification_provider.go @@ -70,7 +70,7 @@ var ( } ) -func (q *Queries) NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (_ *DebugNotificationProvider, err error) { +func (q *Queries) NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (provider *DebugNotificationProvider, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -90,8 +90,11 @@ func (q *Queries) NotificationProviderByIDAndType(ctx context.Context, aggID str return nil, errors.ThrowInternal(err, "QUERY-f9jSf", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + provider, err = scan(row) + return err + }, stmt, args...) + return provider, err } func prepareDebugNotificationProviderQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*DebugNotificationProvider, error)) { diff --git a/internal/query/notification_provider_test.go b/internal/query/notification_provider_test.go index 2fafe45ea..b0bfbe511 100644 --- a/internal/query/notification_provider_test.go +++ b/internal/query/notification_provider_test.go @@ -50,7 +50,7 @@ func Test_NotificationProviderPrepares(t *testing.T) { name: "prepareNotificationProviderQuery no result", prepare: prepareDebugNotificationProviderQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareNotificationProviderStmt), nil, nil, @@ -109,7 +109,7 @@ func Test_NotificationProviderPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*DebugNotificationProvider)(nil), }, } for _, tt := range tests { diff --git a/internal/query/oidc_settings.go b/internal/query/oidc_settings.go index af26406cc..7e48ff43a 100644 --- a/internal/query/oidc_settings.go +++ b/internal/query/oidc_settings.go @@ -75,7 +75,7 @@ type OIDCSettings struct { RefreshTokenExpiration time.Duration } -func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (_ *OIDCSettings, err error) { +func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (settings *OIDCSettings, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -88,8 +88,11 @@ func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) ( return nil, errors.ThrowInternal(err, "QUERY-s9nle", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + settings, err = scan(row) + return err + }, query, args...) + return settings, err } func prepareOIDCSettingsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*OIDCSettings, error)) { diff --git a/internal/query/oidc_settings_test.go b/internal/query/oidc_settings_test.go index 40f544795..d5aa65316 100644 --- a/internal/query/oidc_settings_test.go +++ b/internal/query/oidc_settings_test.go @@ -52,7 +52,7 @@ func Test_OIDCConfigsPrepares(t *testing.T) { name: "prepareOIDCSettingsQuery no result", prepare: prepareOIDCSettingsQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( prepareOIDCSettingsStmt, nil, nil, @@ -113,7 +113,7 @@ func Test_OIDCConfigsPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*OIDCSettings)(nil), }, } for _, tt := range tests { diff --git a/internal/query/org.go b/internal/query/org.go index 40bcbe400..6ba4b54dc 100644 --- a/internal/query/org.go +++ b/internal/query/org.go @@ -89,7 +89,7 @@ func (q *OrgSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return query } -func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (_ *Org, err error) { +func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (org *Org, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -106,11 +106,14 @@ func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string return nil, errors.ThrowInternal(err, "QUERY-AWx52", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + org, err = scan(row) + return err + }, query, args...) + return org, err } -func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (_ *Org, err error) { +func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (org *Org, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -124,11 +127,14 @@ func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (_ *Org return nil, errors.ThrowInternal(err, "QUERY-TYUCE", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + org, err = scan(row) + return err + }, query, args...) + return org, err } -func (q *Queries) OrgByVerifiedDomain(ctx context.Context, domain string) (_ *Org, err error) { +func (q *Queries) OrgByVerifiedDomain(ctx context.Context, domain string) (org *Org, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -142,8 +148,11 @@ func (q *Queries) OrgByVerifiedDomain(ctx context.Context, domain string) (_ *Or return nil, errors.ThrowInternal(err, "QUERY-TYUCE", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + org, err = scan(row) + return err + }, query, args...) + return org, err } func (q *Queries) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) { @@ -176,8 +185,11 @@ func (q *Queries) IsOrgUnique(ctx context.Context, name, domain string) (isUniqu return false, errors.ThrowInternal(err, "QUERY-Dgbe2", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + isUnique, err = scan(row) + return err + }, stmt, args...) + return isUnique, err } func (q *Queries) ExistsOrg(ctx context.Context, id, domain string) (verifiedID string, err error) { @@ -209,14 +221,14 @@ func (q *Queries) SearchOrgs(ctx context.Context, queries *OrgSearchQueries) (or return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + orgs, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.Internal") } - orgs, err = scan(rows) - if err != nil { - return nil, err - } + orgs.LatestSequence, err = q.latestSequence(ctx, orgsTable) return orgs, err } diff --git a/internal/query/org_domain.go b/internal/query/org_domain.go index a901e2d4c..ee89efba9 100644 --- a/internal/query/org_domain.go +++ b/internal/query/org_domain.go @@ -70,14 +70,14 @@ func (q *Queries) SearchOrgDomains(ctx context.Context, queries *OrgDomainSearch return nil, errors.ThrowInvalidArgument(err, "QUERY-ZRfj1", "Errors.Query.SQLStatement") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + domains, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.Internal") } - domains, err = scan(rows) - if err != nil { - return nil, err - } + domains.LatestSequence, err = q.latestSequence(ctx, orgDomainsTable) return domains, err } diff --git a/internal/query/org_domain_test.go b/internal/query/org_domain_test.go index 18bd81704..5757eda65 100644 --- a/internal/query/org_domain_test.go +++ b/internal/query/org_domain_test.go @@ -172,7 +172,7 @@ func Test_OrgDomainPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Domains)(nil), }, } for _, tt := range tests { diff --git a/internal/query/org_member.go b/internal/query/org_member.go index 91bd2d30f..3b5eb4a41 100644 --- a/internal/query/org_member.go +++ b/internal/query/org_member.go @@ -78,7 +78,7 @@ func addOrgMemberWithoutOwnerRemoved(eq map[string]interface{}) { eq[OrgMemberOwnerRemovedUser.identifier()] = false } -func (q *Queries) OrgMembers(ctx context.Context, queries *OrgMembersQuery, withOwnerRemoved bool) (_ *Members, err error) { +func (q *Queries) OrgMembers(ctx context.Context, queries *OrgMembersQuery, withOwnerRemoved bool) (members *Members, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -98,14 +98,14 @@ func (q *Queries) OrgMembers(ctx context.Context, queries *OrgMembersQuery, with return nil, err } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + members, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-5g4yV", "Errors.Internal") } - members, err := scan(rows) - if err != nil { - return nil, err - } + members.LatestSequence = currentSequence return members, err } diff --git a/internal/query/org_member_test.go b/internal/query/org_member_test.go index a8bb7739d..57e9f84b6 100644 --- a/internal/query/org_member_test.go +++ b/internal/query/org_member_test.go @@ -284,7 +284,7 @@ func Test_OrgMemberPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*OrgMembership)(nil), }, } for _, tt := range tests { diff --git a/internal/query/org_metadata.go b/internal/query/org_metadata.go index 5ce36369e..517d2d634 100644 --- a/internal/query/org_metadata.go +++ b/internal/query/org_metadata.go @@ -77,7 +77,7 @@ var ( } ) -func (q *Queries) GetOrgMetadataByKey(ctx context.Context, shouldTriggerBulk bool, orgID string, key string, withOwnerRemoved bool, queries ...SearchQuery) (_ *OrgMetadata, err error) { +func (q *Queries) GetOrgMetadataByKey(ctx context.Context, shouldTriggerBulk bool, orgID string, key string, withOwnerRemoved bool, queries ...SearchQuery) (metadata *OrgMetadata, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -102,11 +102,14 @@ func (q *Queries) GetOrgMetadataByKey(ctx context.Context, shouldTriggerBulk boo return nil, errors.ThrowInternal(err, "QUERY-aDaG2", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + metadata, err = scan(row) + return err + }, stmt, args...) + return metadata, err } -func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, orgID string, queries *OrgMetadataSearchQueries, withOwnerRemoved bool) (_ *OrgMetadataList, err error) { +func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, orgID string, queries *OrgMetadataSearchQueries, withOwnerRemoved bool) (metadata *OrgMetadataList, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -126,14 +129,14 @@ func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, return nil, errors.ThrowInternal(err, "QUERY-Egbld", "Errors.Query.SQLStatment") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + metadata, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Ho2wf", "Errors.Internal") } - metadata, err := scan(rows) - if err != nil { - return nil, err - } + metadata.LatestSequence, err = q.latestSequence(ctx, orgMetadataTable) return metadata, err } diff --git a/internal/query/org_metadata_test.go b/internal/query/org_metadata_test.go index 4bfee4841..600a2cca4 100644 --- a/internal/query/org_metadata_test.go +++ b/internal/query/org_metadata_test.go @@ -63,7 +63,7 @@ func Test_OrgMetadataPrepares(t *testing.T) { name: "prepareOrgMetadataQuery no result", prepare: prepareOrgMetadataQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(orgMetadataQuery), nil, nil, @@ -118,7 +118,7 @@ func Test_OrgMetadataPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*OrgMetadata)(nil), }, { name: "prepareOrgMetadataListQuery no result", @@ -239,7 +239,7 @@ func Test_OrgMetadataPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*OrgMetadataList)(nil), }, } for _, tt := range tests { diff --git a/internal/query/org_test.go b/internal/query/org_test.go index 194c7bd13..c4c2c21ad 100644 --- a/internal/query/org_test.go +++ b/internal/query/org_test.go @@ -209,13 +209,13 @@ func Test_OrgPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Orgs)(nil), }, { name: "prepareOrgQuery no result", prepare: prepareOrgQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareOrgQueryStmt), nil, nil, @@ -274,13 +274,13 @@ func Test_OrgPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Org)(nil), }, { name: "prepareOrgUniqueQuery no result", prepare: prepareOrgUniqueQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(prepareOrgUniqueStmt), nil, nil, @@ -323,7 +323,7 @@ func Test_OrgPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: false, }, } for _, tt := range tests { diff --git a/internal/query/password_age_policy.go b/internal/query/password_age_policy.go index fda59e88e..e2130f776 100644 --- a/internal/query/password_age_policy.go +++ b/internal/query/password_age_policy.go @@ -81,7 +81,7 @@ var ( } ) -func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *PasswordAgePolicy, err error) { +func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *PasswordAgePolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -107,11 +107,14 @@ func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, shouldTriggerBulk return nil, errors.ThrowInternal(err, "QUERY-SKR6X", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultPasswordAgePolicy(ctx context.Context, shouldTriggerBulk bool) (_ *PasswordAgePolicy, err error) { +func (q *Queries) DefaultPasswordAgePolicy(ctx context.Context, shouldTriggerBulk bool) (policy *PasswordAgePolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -129,8 +132,11 @@ func (q *Queries) DefaultPasswordAgePolicy(ctx context.Context, shouldTriggerBul return nil, errors.ThrowInternal(err, "QUERY-mN0Ci", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func preparePasswordAgePolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*PasswordAgePolicy, error)) { diff --git a/internal/query/password_age_policy_test.go b/internal/query/password_age_policy_test.go index 15fbfe593..738a8bb82 100644 --- a/internal/query/password_age_policy_test.go +++ b/internal/query/password_age_policy_test.go @@ -52,7 +52,7 @@ func Test_PasswordAgePolicyPrepares(t *testing.T) { name: "preparePasswordAgePolicyQuery no result", prepare: preparePasswordAgePolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(preparePasswordAgePolicyStmt), nil, nil, @@ -113,7 +113,7 @@ func Test_PasswordAgePolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PasswordAgePolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/password_complexity_policy.go b/internal/query/password_complexity_policy.go index e5f15f181..a61048b99 100644 --- a/internal/query/password_complexity_policy.go +++ b/internal/query/password_complexity_policy.go @@ -33,7 +33,7 @@ type PasswordComplexityPolicy struct { IsDefault bool } -func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *PasswordComplexityPolicy, err error) { +func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *PasswordComplexityPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -59,11 +59,14 @@ func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, shouldTrigg return nil, errors.ThrowInternal(err, "QUERY-lDnrk", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultPasswordComplexityPolicy(ctx context.Context, shouldTriggerBulk bool) (_ *PasswordComplexityPolicy, err error) { +func (q *Queries) DefaultPasswordComplexityPolicy(ctx context.Context, shouldTriggerBulk bool) (policy *PasswordComplexityPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -82,8 +85,11 @@ func (q *Queries) DefaultPasswordComplexityPolicy(ctx context.Context, shouldTri return nil, errors.ThrowInternal(err, "QUERY-h4Uyr", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } var ( diff --git a/internal/query/password_complexity_policy_test.go b/internal/query/password_complexity_policy_test.go index 7629940fc..3f3743831 100644 --- a/internal/query/password_complexity_policy_test.go +++ b/internal/query/password_complexity_policy_test.go @@ -58,7 +58,7 @@ func Test_PasswordComplexityPolicyPrepares(t *testing.T) { name: "preparePasswordComplexityPolicyQuery no result", prepare: preparePasswordComplexityPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(preparePasswordComplexityPolicyStmt), nil, nil, @@ -125,7 +125,7 @@ func Test_PasswordComplexityPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PasswordComplexityPolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/prepare_test.go b/internal/query/prepare_test.go index cb69fb21e..242b38740 100644 --- a/internal/query/prepare_test.go +++ b/internal/query/prepare_test.go @@ -15,6 +15,7 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/database" ) var ( @@ -53,14 +54,15 @@ func assertPrepare(t *testing.T, prepareFunc, expectedObject interface{}, sqlExp } return isErr(err) } - object, ok := execScan(client, builder, scan, errCheck) + object, ok, didScan := execScan(&database.DB{DB: client}, builder, scan, errCheck) if !ok { t.Error(object) return false } - - if !assert.Equal(t, expectedObject, object) { - return false + if didScan { + if !assert.Equal(t, expectedObject, object) { + return false + } } if err := mock.ExpectationsWereMet(); err != nil { @@ -77,7 +79,23 @@ type sqlExpectation func(sqlmock.Sqlmock) sqlmock.Sqlmock func mockQuery(stmt string, cols []string, row []driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) sqlmock.Sqlmock { return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + m.ExpectBegin() q := m.ExpectQuery(stmt).WithArgs(args...) + m.ExpectCommit() + result := sqlmock.NewRows(cols) + if len(row) > 0 { + result.AddRow(row...) + } + q.WillReturnRows(result) + return m + } +} + +func mockQueryScanErr(stmt string, cols []string, row []driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + m.ExpectBegin() + q := m.ExpectQuery(stmt).WithArgs(args...) + m.ExpectRollback() result := sqlmock.NewRows(cols) if len(row) > 0 { result.AddRow(row...) @@ -89,7 +107,28 @@ func mockQuery(stmt string, cols []string, row []driver.Value, args ...driver.Va func mockQueries(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) sqlmock.Sqlmock { return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + m.ExpectBegin() q := m.ExpectQuery(stmt).WithArgs(args...) + m.ExpectCommit() + result := sqlmock.NewRows(cols) + count := uint64(len(rows)) + for _, row := range rows { + if cols[len(cols)-1] == "count" { + row = append(row, count) + } + result.AddRow(row...) + } + q.WillReturnRows(result) + q.RowsWillBeClosed() + return m + } +} + +func mockQueriesScanErr(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + m.ExpectBegin() + q := m.ExpectQuery(stmt).WithArgs(args...) + m.ExpectRollback() result := sqlmock.NewRows(cols) count := uint64(len(rows)) for _, row := range rows { @@ -106,8 +145,10 @@ func mockQueries(stmt string, cols []string, rows [][]driver.Value, args ...driv func mockQueryErr(stmt string, err error, args ...driver.Value) func(m sqlmock.Sqlmock) sqlmock.Sqlmock { return func(m sqlmock.Sqlmock) sqlmock.Sqlmock { + m.ExpectBegin() q := m.ExpectQuery(stmt).WithArgs(args...) q.WillReturnError(err) + m.ExpectRollback() return m } } @@ -127,52 +168,65 @@ var ( selectBuilderType = reflect.TypeOf(sq.SelectBuilder{}) ) -func execScan(client *sql.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (interface{}, bool) { +func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (object interface{}, ok bool, didScan bool) { scanType := reflect.TypeOf(scan) err := validateScan(scanType) if err != nil { - return err, false + return err, false, false } stmt, args, err := builder.ToSql() if err != nil { - return fmt.Errorf("unexpeted error from sql builder: %w", err), false + return fmt.Errorf("unexpeted error from sql builder: %w", err), false, false } //resultSet represents *sql.Row or *sql.Rows, // depending on whats assignable to the scan function - var resultSet interface{} + var res []reflect.Value //execute sql stmt // if scan(*sql.Rows)... if scanType.In(0).AssignableTo(rowsType) { - resultSet, err = client.Query(stmt, args...) - if err != nil { - return errCheck(err) - } + err = client.Query(func(rows *sql.Rows) error { + didScan = true + res = reflect.ValueOf(scan).Call([]reflect.Value{reflect.ValueOf(rows)}) + if err, ok := res[1].Interface().(error); ok { + return err + } + return nil + }, stmt, args...) // if scan(*sql.Row)... } else if scanType.In(0).AssignableTo(rowType) { - row := client.QueryRow(stmt, args...) - if row.Err() != nil { - return errCheck(row.Err()) - } - resultSet = row + err = client.QueryRow(func(r *sql.Row) error { + didScan = true + res = reflect.ValueOf(scan).Call([]reflect.Value{reflect.ValueOf(r)}) + if err, ok := res[1].Interface().(error); ok { + return err + } + return nil + }, stmt, args...) + } else { - return errors.New("scan: parameter must be *sql.Row or *sql.Rows"), false + return errors.New("scan: parameter must be *sql.Row or *sql.Rows"), false, false } - // res contains object and error - res := reflect.ValueOf(scan).Call([]reflect.Value{reflect.ValueOf(resultSet)}) + if err != nil { + err, ok := errCheck(err) + if didScan { + return res[0].Interface(), ok, didScan + } + return err, ok, didScan + } //check for error if res[1].Interface() != nil { if err, ok := errCheck(res[1].Interface().(error)); !ok { - return fmt.Errorf("scan failed: %w", err), false + return fmt.Errorf("scan failed: %w", err), false, didScan } } - return res[0].Interface(), true + return res[0].Interface(), true, didScan } func validateScan(scanType reflect.Type) error { diff --git a/internal/query/privacy_policy.go b/internal/query/privacy_policy.go index e207c3e9b..8ff9f0edb 100644 --- a/internal/query/privacy_policy.go +++ b/internal/query/privacy_policy.go @@ -91,7 +91,7 @@ var ( } ) -func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (_ *PrivacyPolicy, err error) { +func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *PrivacyPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -116,11 +116,14 @@ func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, shouldTriggerBulk bool return nil, errors.ThrowInternal(err, "QUERY-UXuPI", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } -func (q *Queries) DefaultPrivacyPolicy(ctx context.Context, shouldTriggerBulk bool) (_ *PrivacyPolicy, err error) { +func (q *Queries) DefaultPrivacyPolicy(ctx context.Context, shouldTriggerBulk bool) (policy *PrivacyPolicy, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -139,8 +142,11 @@ func (q *Queries) DefaultPrivacyPolicy(ctx context.Context, shouldTriggerBulk bo return nil, errors.ThrowInternal(err, "QUERY-LkFZ7", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func preparePrivacyPolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*PrivacyPolicy, error)) { diff --git a/internal/query/privacy_policy_test.go b/internal/query/privacy_policy_test.go index ea68af11b..70b32723d 100644 --- a/internal/query/privacy_policy_test.go +++ b/internal/query/privacy_policy_test.go @@ -56,7 +56,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { name: "preparePrivacyPolicyQuery no result", prepare: preparePrivacyPolicyQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(preparePrivacyPolicyStmt), nil, nil, @@ -121,7 +121,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PrivacyPolicy)(nil), }, } for _, tt := range tests { diff --git a/internal/query/project.go b/internal/query/project.go index 065a3e5c5..0e628d7b1 100644 --- a/internal/query/project.go +++ b/internal/query/project.go @@ -100,7 +100,7 @@ type ProjectSearchQueries struct { Queries []SearchQuery } -func (q *Queries) ProjectByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool) (_ *Project, err error) { +func (q *Queries) ProjectByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool) (project *Project, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -121,8 +121,11 @@ func (q *Queries) ProjectByID(ctx context.Context, shouldTriggerBulk bool, id st return nil, errors.ThrowInternal(err, "QUERY-2m00Q", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + project, err = scan(row) + return err + }, query, args...) + return project, err } func (q *Queries) SearchProjects(ctx context.Context, queries *ProjectSearchQueries, withOwnerRemoved bool) (projects *Projects, err error) { @@ -139,14 +142,13 @@ func (q *Queries) SearchProjects(ctx context.Context, queries *ProjectSearchQuer return nil, errors.ThrowInvalidArgument(err, "QUERY-fn9ew", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + projects, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-2j00f", "Errors.Internal") } - projects, err = scan(rows) - if err != nil { - return nil, err - } projects.LatestSequence, err = q.latestSequence(ctx, projectsTable) return projects, err } diff --git a/internal/query/project_grant.go b/internal/query/project_grant.go index fee8ae836..d7ee5ce1a 100644 --- a/internal/query/project_grant.go +++ b/internal/query/project_grant.go @@ -111,7 +111,7 @@ type ProjectGrantSearchQueries struct { Queries []SearchQuery } -func (q *Queries) ProjectGrantByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool) (_ *ProjectGrant, err error) { +func (q *Queries) ProjectGrantByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool) (grant *ProjectGrant, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -133,11 +133,14 @@ func (q *Queries) ProjectGrantByID(ctx context.Context, shouldTriggerBulk bool, return nil, errors.ThrowInternal(err, "QUERY-Nf93d", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + grant, err = scan(row) + return err + }, query, args...) + return grant, err } -func (q *Queries) ProjectGrantByIDAndGrantedOrg(ctx context.Context, id, grantedOrg string, withOwnerRemoved bool) (_ *ProjectGrant, err error) { +func (q *Queries) ProjectGrantByIDAndGrantedOrg(ctx context.Context, id, grantedOrg string, withOwnerRemoved bool) (grant *ProjectGrant, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -156,11 +159,14 @@ func (q *Queries) ProjectGrantByIDAndGrantedOrg(ctx context.Context, id, granted return nil, errors.ThrowInternal(err, "QUERY-MO9fs", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + grant, err = scan(row) + return err + }, query, args...) + return grant, err } -func (q *Queries) SearchProjectGrants(ctx context.Context, queries *ProjectGrantSearchQueries, withOwnerRemoved bool) (projects *ProjectGrants, err error) { +func (q *Queries) SearchProjectGrants(ctx context.Context, queries *ProjectGrantSearchQueries, withOwnerRemoved bool) (grants *ProjectGrants, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -177,16 +183,16 @@ func (q *Queries) SearchProjectGrants(ctx context.Context, queries *ProjectGrant return nil, errors.ThrowInvalidArgument(err, "QUERY-N9fsg", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + grants, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-PP02n", "Errors.Internal") } - projects, err = scan(rows) - if err != nil { - return nil, err - } - projects.LatestSequence, err = q.latestSequence(ctx, projectGrantsTable) - return projects, err + + grants.LatestSequence, err = q.latestSequence(ctx, projectGrantsTable) + return grants, err } func (q *Queries) SearchProjectGrantsByProjectIDAndRoleKey(ctx context.Context, projectID, roleKey string, withOwnerRemoved bool) (projects *ProjectGrants, err error) { diff --git a/internal/query/project_grant_member.go b/internal/query/project_grant_member.go index 0f2167908..11de7cd0e 100644 --- a/internal/query/project_grant_member.go +++ b/internal/query/project_grant_member.go @@ -95,7 +95,7 @@ func addProjectGrantMemberWithoutOwnerRemoved(eq map[string]interface{}) { eq[ProjectGrantMemberGrantedOrgRemoved.identifier()] = false } -func (q *Queries) ProjectGrantMembers(ctx context.Context, queries *ProjectGrantMembersQuery, withOwnerRemoved bool) (*Members, error) { +func (q *Queries) ProjectGrantMembers(ctx context.Context, queries *ProjectGrantMembersQuery, withOwnerRemoved bool) (members *Members, err error) { query, scan := prepareProjectGrantMembersQuery(ctx, q.client) eq := sq.Eq{ProjectGrantMemberInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} if !withOwnerRemoved { @@ -112,14 +112,14 @@ func (q *Queries) ProjectGrantMembers(ctx context.Context, queries *ProjectGrant return nil, err } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + members, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Pdg1I", "Errors.Internal") } - members, err := scan(rows) - if err != nil { - return nil, err - } + members.LatestSequence = currentSequence return members, err } diff --git a/internal/query/project_grant_member_test.go b/internal/query/project_grant_member_test.go index 686fe778a..99e7c5320 100644 --- a/internal/query/project_grant_member_test.go +++ b/internal/query/project_grant_member_test.go @@ -287,7 +287,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*ProjectGrantMembership)(nil), }, } for _, tt := range tests { diff --git a/internal/query/project_grant_test.go b/internal/query/project_grant_test.go index 65de48a66..b504cf1fb 100644 --- a/internal/query/project_grant_test.go +++ b/internal/query/project_grant_test.go @@ -381,13 +381,13 @@ func Test_ProjectGrantPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*ProjectGrants)(nil), }, { name: "prepareProjectGrantQuery no result", prepare: prepareProjectGrantQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( regexp.QuoteMeta(projectGrantQuery), nil, nil, @@ -568,7 +568,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*ProjectGrant)(nil), }, } for _, tt := range tests { diff --git a/internal/query/project_member.go b/internal/query/project_member.go index 15d5f8325..3cdb30086 100644 --- a/internal/query/project_member.go +++ b/internal/query/project_member.go @@ -78,7 +78,7 @@ func addProjectMemberWithoutOwnerRemoved(eq map[string]interface{}) { eq[ProjectMemberOwnerRemovedUser.identifier()] = false } -func (q *Queries) ProjectMembers(ctx context.Context, queries *ProjectMembersQuery, withOwnerRemoved bool) (_ *Members, err error) { +func (q *Queries) ProjectMembers(ctx context.Context, queries *ProjectMembersQuery, withOwnerRemoved bool) (members *Members, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -98,14 +98,14 @@ func (q *Queries) ProjectMembers(ctx context.Context, queries *ProjectMembersQue return nil, err } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + members, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-uh6pj", "Errors.Internal") } - members, err := scan(rows) - if err != nil { - return nil, err - } + members.LatestSequence = currentSequence return members, err } diff --git a/internal/query/project_member_test.go b/internal/query/project_member_test.go index fa9181d35..f91771889 100644 --- a/internal/query/project_member_test.go +++ b/internal/query/project_member_test.go @@ -284,7 +284,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*ProjectMembership)(nil), }, } for _, tt := range tests { diff --git a/internal/query/project_role.go b/internal/query/project_role.go index 7365b1247..fbe802714 100644 --- a/internal/query/project_role.go +++ b/internal/query/project_role.go @@ -83,7 +83,7 @@ type ProjectRoleSearchQueries struct { Queries []SearchQuery } -func (q *Queries) SearchProjectRoles(ctx context.Context, shouldTriggerBulk bool, queries *ProjectRoleSearchQueries, withOwnerRemoved bool) (projects *ProjectRoles, err error) { +func (q *Queries) SearchProjectRoles(ctx context.Context, shouldTriggerBulk bool, queries *ProjectRoleSearchQueries, withOwnerRemoved bool) (roles *ProjectRoles, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -102,19 +102,18 @@ func (q *Queries) SearchProjectRoles(ctx context.Context, shouldTriggerBulk bool return nil, errors.ThrowInvalidArgument(err, "QUERY-3N9ff", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + roles, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-5Ngd9", "Errors.Internal") } - projects, err = scan(rows) - if err != nil { - return nil, err - } - projects.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) - return projects, err + roles.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) + return roles, err } -func (q *Queries) SearchGrantedProjectRoles(ctx context.Context, grantID, grantedOrg string, queries *ProjectRoleSearchQueries, withOwnerRemoved bool) (projects *ProjectRoles, err error) { +func (q *Queries) SearchGrantedProjectRoles(ctx context.Context, grantID, grantedOrg string, queries *ProjectRoleSearchQueries, withOwnerRemoved bool) (roles *ProjectRoles, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -138,16 +137,16 @@ func (q *Queries) SearchGrantedProjectRoles(ctx context.Context, grantID, grante return nil, errors.ThrowInvalidArgument(err, "QUERY-3N9ff", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + roles, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-5Ngd9", "Errors.Internal") } - projects, err = scan(rows) - if err != nil { - return nil, err - } - projects.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) - return projects, err + + roles.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) + return roles, err } func NewProjectRoleProjectIDSearchQuery(value string) (SearchQuery, error) { diff --git a/internal/query/project_role_test.go b/internal/query/project_role_test.go index 9324cf292..dbc646d4d 100644 --- a/internal/query/project_role_test.go +++ b/internal/query/project_role_test.go @@ -170,7 +170,7 @@ func Test_ProjectRolePrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*ProjectRoles)(nil), }, } for _, tt := range tests { diff --git a/internal/query/project_test.go b/internal/query/project_test.go index ea96f62c4..4d4bd72ea 100644 --- a/internal/query/project_test.go +++ b/internal/query/project_test.go @@ -238,13 +238,13 @@ func Test_ProjectPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Projects)(nil), }, { name: "prepareProjectQuery no result", prepare: prepareProjectQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( prepareProjectStmt, nil, nil, @@ -309,7 +309,7 @@ func Test_ProjectPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Project)(nil), }, } for _, tt := range tests { diff --git a/internal/query/secret_generator_test.go b/internal/query/secret_generator_test.go index 2d84c9b1c..44d2015c1 100644 --- a/internal/query/secret_generator_test.go +++ b/internal/query/secret_generator_test.go @@ -234,13 +234,13 @@ func Test_SecretGeneratorsPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SecretGenerators)(nil), }, { name: "prepareSecretGeneratorQuery no result", prepare: prepareSecretGeneratorQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( prepareSecretGeneratorStmt, nil, nil, @@ -307,7 +307,7 @@ func Test_SecretGeneratorsPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SecretGenerator)(nil), }, } for _, tt := range tests { diff --git a/internal/query/secret_generators.go b/internal/query/secret_generators.go index a4859279f..35c9ccbd8 100644 --- a/internal/query/secret_generators.go +++ b/internal/query/secret_generators.go @@ -134,7 +134,7 @@ func (q *Queries) InitHashGenerator(ctx context.Context, generatorType domain.Se return crypto.NewHashGenerator(cryptoConfig, algorithm), nil } -func (q *Queries) SecretGeneratorByType(ctx context.Context, generatorType domain.SecretGeneratorType) (_ *SecretGenerator, err error) { +func (q *Queries) SecretGeneratorByType(ctx context.Context, generatorType domain.SecretGeneratorType) (generator *SecretGenerator, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -147,8 +147,11 @@ func (q *Queries) SecretGeneratorByType(ctx context.Context, generatorType domai return nil, errors.ThrowInternal(err, "QUERY-3k99f", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + generator, err = scan(row) + return err + }, query, args...) + return generator, err } func (q *Queries) SearchSecretGenerators(ctx context.Context, queries *SecretGeneratorSearchQueries) (secretGenerators *SecretGenerators, err error) { @@ -164,14 +167,13 @@ func (q *Queries) SearchSecretGenerators(ctx context.Context, queries *SecretGen return nil, errors.ThrowInvalidArgument(err, "QUERY-sn9lw", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + secretGenerators, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-4miii", "Errors.Internal") } - secretGenerators, err = scan(rows) - if err != nil { - return nil, err - } secretGenerators.LatestSequence, err = q.latestSequence(ctx, secretGeneratorsTable) return secretGenerators, err } diff --git a/internal/query/security_policy.go b/internal/query/security_policy.go index 29e15de21..5a1b23dfb 100644 --- a/internal/query/security_policy.go +++ b/internal/query/security_policy.go @@ -57,7 +57,7 @@ type SecurityPolicy struct { AllowedOrigins database.StringArray } -func (q *Queries) SecurityPolicy(ctx context.Context) (*SecurityPolicy, error) { +func (q *Queries) SecurityPolicy(ctx context.Context) (policy *SecurityPolicy, err error) { stmt, scan := prepareSecurityPolicyQuery(ctx, q.client) query, args, err := stmt.Where(sq.Eq{ SecurityPolicyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), @@ -66,8 +66,11 @@ func (q *Queries) SecurityPolicy(ctx context.Context) (*SecurityPolicy, error) { return nil, errors.ThrowInternal(err, "QUERY-Sf6d1", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + policy, err = scan(row) + return err + }, query, args...) + return policy, err } func prepareSecurityPolicyQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*SecurityPolicy, error)) { diff --git a/internal/query/session.go b/internal/query/session.go index 0c7d67dbf..2a1672a3f 100644 --- a/internal/query/session.go +++ b/internal/query/session.go @@ -151,7 +151,7 @@ var ( } ) -func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string) (_ *Session, err error) { +func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string) (session *Session, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -170,8 +170,11 @@ func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, s return nil, errors.ThrowInternal(err, "QUERY-dn9JW", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - session, tokenID, err := scan(row) + var tokenID string + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + session, tokenID, err = scan(row) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -184,7 +187,7 @@ func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, s return session, nil } -func (q *Queries) SearchSessions(ctx context.Context, queries *SessionsSearchQueries) (_ *Sessions, err error) { +func (q *Queries) SearchSessions(ctx context.Context, queries *SessionsSearchQueries) (sessions *Sessions, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -197,14 +200,14 @@ func (q *Queries) SearchSessions(ctx context.Context, queries *SessionsSearchQue return nil, errors.ThrowInvalidArgument(err, "QUERY-sn9Jf", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil || rows.Err() != nil { + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + sessions, err = scan(rows) + return err + }, stmt, args...) + if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Sfg42", "Errors.Internal") } - sessions, err := scan(rows) - if err != nil { - return nil, err - } + sessions.LatestSequence, err = q.latestSequence(ctx, sessionsTable) return sessions, err } diff --git a/internal/query/sessions_test.go b/internal/query/sessions_test.go index bc9e8bc6a..c66868a9d 100644 --- a/internal/query/sessions_test.go +++ b/internal/query/sessions_test.go @@ -343,7 +343,7 @@ func Test_SessionsPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Sessions)(nil), }, } for _, tt := range tests { @@ -368,7 +368,7 @@ func Test_SessionPrepare(t *testing.T) { name: "prepareSessionQuery no result", prepare: prepareSessionQueryTesting(t, ""), want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( expectedSessionQuery, nil, nil, @@ -460,7 +460,7 @@ func Test_SessionPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Session)(nil), }, } for _, tt := range tests { diff --git a/internal/query/sms.go b/internal/query/sms.go index 6b0273ed2..956328b64 100644 --- a/internal/query/sms.go +++ b/internal/query/sms.go @@ -115,7 +115,7 @@ var ( } ) -func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (_ *SMSConfig, err error) { +func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (config *SMSConfig, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -130,11 +130,14 @@ func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (_ *SMSC return nil, errors.ThrowInternal(err, "QUERY-dn9JW", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + config, err = scan(row) + return err + }, stmt, args...) + return config, err } -func (q *Queries) SMSProviderConfig(ctx context.Context, queries ...SearchQuery) (_ *SMSConfig, err error) { +func (q *Queries) SMSProviderConfig(ctx context.Context, queries ...SearchQuery) (config *SMSConfig, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -151,11 +154,14 @@ func (q *Queries) SMSProviderConfig(ctx context.Context, queries ...SearchQuery) return nil, errors.ThrowInternal(err, "QUERY-dn9JW", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + config, err = scan(row) + return err + }, stmt, args...) + return config, err } -func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearchQueries) (_ *SMSConfigs, err error) { +func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearchQueries) (configs *SMSConfigs, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -168,16 +174,15 @@ func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearc return nil, errors.ThrowInvalidArgument(err, "QUERY-sn9Jf", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + configs, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal") } - apps, err := scan(rows) - if err != nil { - return nil, err - } - apps.LatestSequence, err = q.latestSequence(ctx, smsConfigsTable) - return apps, err + configs.LatestSequence, err = q.latestSequence(ctx, smsConfigsTable) + return configs, err } func NewSMSProviderStateQuery(state domain.SMSConfigState) (SearchQuery, error) { diff --git a/internal/query/sms_test.go b/internal/query/sms_test.go index 88b0516de..9f6c906c7 100644 --- a/internal/query/sms_test.go +++ b/internal/query/sms_test.go @@ -225,7 +225,7 @@ func Test_SMSConfigssPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SMSConfigs)(nil), }, } for _, tt := range tests { @@ -250,7 +250,7 @@ func Test_SMSConfigPrepare(t *testing.T) { name: "prepareSMSConfigQuery no result", prepare: prepareSMSConfigQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( expectedSMSConfigQuery, nil, nil, @@ -317,7 +317,7 @@ func Test_SMSConfigPrepare(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SMSConfig)(nil), }, } for _, tt := range tests { diff --git a/internal/query/smtp.go b/internal/query/smtp.go index 4b0f0a019..53db6f7b2 100644 --- a/internal/query/smtp.go +++ b/internal/query/smtp.go @@ -91,7 +91,7 @@ type SMTPConfig struct { Password *crypto.CryptoValue } -func (q *Queries) SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (_ *SMTPConfig, err error) { +func (q *Queries) SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (config *SMTPConfig, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -104,8 +104,11 @@ func (q *Queries) SMTPConfigByAggregateID(ctx context.Context, aggregateID strin return nil, errors.ThrowInternal(err, "QUERY-3m9sl", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, query, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + config, err = scan(row) + return err + }, query, args...) + return config, err } func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*SMTPConfig, error)) { diff --git a/internal/query/smtp_test.go b/internal/query/smtp_test.go index 77ef763ed..899c3ba4e 100644 --- a/internal/query/smtp_test.go +++ b/internal/query/smtp_test.go @@ -56,7 +56,7 @@ func Test_SMTPConfigsPrepares(t *testing.T) { name: "prepareSMTPConfigQuery no result", prepare: prepareSMTPConfigQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( prepareSMTPConfigStmt, nil, nil, @@ -121,7 +121,7 @@ func Test_SMTPConfigsPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*SMTPConfig)(nil), }, } for _, tt := range tests { diff --git a/internal/query/user.go b/internal/query/user.go index c335129ed..9b421bdc9 100644 --- a/internal/query/user.go +++ b/internal/query/user.go @@ -333,7 +333,7 @@ func addUserWithoutOwnerRemoved(eq map[string]interface{}) { eq[userPreferredLoginNameOwnerRemovedDomainCol.identifier()] = false } -func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (_ *User, err error) { +func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (user *User, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -358,11 +358,14 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userI return nil, errors.ThrowInternal(err, "QUERY-FBg21", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + user, err = scan(row) + return err + }, stmt, args...) + return user, err } -func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwnerRemoved bool, queries ...SearchQuery) (_ *User, err error) { +func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwnerRemoved bool, queries ...SearchQuery) (user *User, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -386,11 +389,14 @@ func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwner return nil, errors.ThrowInternal(err, "QUERY-Dnhr2", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + user, err = scan(row) + return err + }, stmt, args...) + return user, err } -func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (_ *Profile, err error) { +func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (profile *Profile, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -410,11 +416,14 @@ func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerR return nil, errors.ThrowInternal(err, "QUERY-Dgbg2", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + profile, err = scan(row) + return err + }, stmt, args...) + return profile, err } -func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (_ *Email, err error) { +func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (email *Email, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -434,11 +443,14 @@ func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRem return nil, errors.ThrowInternal(err, "QUERY-BHhj3", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + email, err = scan(row) + return err + }, stmt, args...) + return email, err } -func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (_ *Phone, err error) { +func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (phone *Phone, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -458,11 +470,14 @@ func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRem return nil, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + phone, err = scan(row) + return err + }, stmt, args...) + return phone, err } -func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (_ *NotifyUser, err error) { +func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (user *NotifyUser, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -487,11 +502,14 @@ func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, u return nil, errors.ThrowInternal(err, "QUERY-Err3g", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + user, err = scan(row) + return err + }, stmt, args...) + return user, err } -func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withOwnerRemoved bool, queries ...SearchQuery) (_ *NotifyUser, err error) { +func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withOwnerRemoved bool, queries ...SearchQuery) (user *NotifyUser, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -515,11 +533,14 @@ func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withO return nil, errors.ThrowInternal(err, "QUERY-Err3g", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + user, err = scan(row) + return err + }, stmt, args...) + return user, err } -func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, withOwnerRemoved bool) (_ *Users, err error) { +func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, withOwnerRemoved bool) (users *Users, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -534,19 +555,19 @@ func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, w return nil, errors.ThrowInternal(err, "QUERY-Dgbg2", "Errors.Query.SQLStatment") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + users, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-AG4gs", "Errors.Internal") } - users, err := scan(rows) - if err != nil { - return nil, err - } + users.LatestSequence, err = q.latestSequence(ctx, userTable) return users, err } -func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwner string, withOwnerRemoved bool) (_ bool, err error) { +func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwner string, withOwnerRemoved bool) (isUnique bool, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -584,8 +605,12 @@ func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwn if err != nil { return false, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + isUnique, err = scan(row) + return err + }, stmt, args...) + return isUnique, err } func (q *UserSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { diff --git a/internal/query/user_auth_method.go b/internal/query/user_auth_method.go index fc80685bc..0881c253c 100644 --- a/internal/query/user_auth_method.go +++ b/internal/query/user_auth_method.go @@ -130,11 +130,10 @@ func (q *Queries) SearchUserAuthMethods(ctx context.Context, queries *UserAuthMe return nil, errors.ThrowInvalidArgument(err, "QUERY-j9NJd", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-3n99f", "Errors.Internal") - } - userAuthMethods, err = scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + userAuthMethods, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -165,11 +164,10 @@ func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID stri return nil, errors.ThrowInvalidArgument(err, "QUERY-Sfdrg", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil || rows.Err() != nil { - return nil, errors.ThrowInternal(err, "QUERY-SDgr3", "Errors.Internal") - } - userAuthMethodTypes, err = scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + userAuthMethodTypes, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } @@ -200,11 +198,14 @@ func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID st return nil, false, false, errors.ThrowInvalidArgument(err, "QUERY-E5ut4", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil || rows.Err() != nil { + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + userAuthMethodTypes, forceMFA, forceMFALocalOnly, err = scan(rows) + return err + }, stmt, args...) + if err != nil { return nil, false, false, errors.ThrowInternal(err, "QUERY-Dun75", "Errors.Internal") } - return scan(rows) + return userAuthMethodTypes, forceMFA, forceMFALocalOnly, nil } func NewUserAuthMethodUserIDSearchQuery(value string) (SearchQuery, error) { diff --git a/internal/query/user_auth_method_test.go b/internal/query/user_auth_method_test.go index 2cfe553e0..978e0e81a 100644 --- a/internal/query/user_auth_method_test.go +++ b/internal/query/user_auth_method_test.go @@ -224,7 +224,7 @@ func Test_UserAuthMethodPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*AuthMethodTypes)(nil), }, { name: "prepareActiveUserAuthMethodTypesQuery no result", @@ -313,7 +313,7 @@ func Test_UserAuthMethodPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*AuthMethodTypes)(nil), }, { name: "prepareUserAuthMethodTypesRequiredQuery no result", diff --git a/internal/query/user_grant.go b/internal/query/user_grant.go index f78cd99d8..9643b3d93 100644 --- a/internal/query/user_grant.go +++ b/internal/query/user_grant.go @@ -232,7 +232,7 @@ func addUserGrantWithoutOwnerRemoved(eq map[string]interface{}) { addLoginNameWithoutOwnerRemoved(eq) } -func (q *Queries) UserGrant(ctx context.Context, shouldTriggerBulk bool, withOwnerRemoved bool, queries ...SearchQuery) (_ *UserGrant, err error) { +func (q *Queries) UserGrant(ctx context.Context, shouldTriggerBulk bool, withOwnerRemoved bool, queries ...SearchQuery) (grant *UserGrant, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -253,11 +253,14 @@ func (q *Queries) UserGrant(ctx context.Context, shouldTriggerBulk bool, withOwn return nil, errors.ThrowInternal(err, "QUERY-Fa1KW", "Errors.Query.SQLStatement") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + grant, err = scan(row) + return err + }, stmt, args...) + return grant, err } -func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, shouldTriggerBulk, withOwnerRemoved bool) (_ *UserGrants, err error) { +func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, shouldTriggerBulk, withOwnerRemoved bool) (grants *UserGrants, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -280,11 +283,10 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh return nil, err } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, err - } - grants, err := scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + grants, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } diff --git a/internal/query/user_grant_test.go b/internal/query/user_grant_test.go index d7fccea10..da54f2008 100644 --- a/internal/query/user_grant_test.go +++ b/internal/query/user_grant_test.go @@ -122,7 +122,7 @@ func Test_UserGrantPrepares(t *testing.T) { name: "prepareUserGrantQuery no result", prepare: prepareUserGrantQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQueriesScanErr( userGrantStmt, nil, nil, @@ -441,7 +441,7 @@ func Test_UserGrantPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*UserGrant)(nil), }, { name: "prepareUserGrantsQuery no result", @@ -920,7 +920,7 @@ func Test_UserGrantPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*UserGrants)(nil), }, } for _, tt := range tests { diff --git a/internal/query/user_membership.go b/internal/query/user_membership.go index f72ef4f08..f2eae22a0 100644 --- a/internal/query/user_membership.go +++ b/internal/query/user_membership.go @@ -105,7 +105,7 @@ func (q *MembershipSearchQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder return query } -func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuery, withOwnerRemoved bool) (_ *Memberships, err error) { +func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuery, withOwnerRemoved bool) (memberships *Memberships, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -121,11 +121,10 @@ func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuer } queryArgs = append(queryArgs, args...) - rows, err := q.client.QueryContext(ctx, stmt, queryArgs...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-eAV2x", "Errors.Internal") - } - memberships, err := scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + memberships, err = scan(rows) + return err + }, stmt, queryArgs...) if err != nil { return nil, err } diff --git a/internal/query/user_membership_test.go b/internal/query/user_membership_test.go index 60708637f..237e21359 100644 --- a/internal/query/user_membership_test.go +++ b/internal/query/user_membership_test.go @@ -444,7 +444,7 @@ func Test_MembershipPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Memberships)(nil), }, } for _, tt := range tests { diff --git a/internal/query/user_metadata.go b/internal/query/user_metadata.go index d0a9202b8..6a2be6a7e 100644 --- a/internal/query/user_metadata.go +++ b/internal/query/user_metadata.go @@ -77,7 +77,7 @@ var ( } ) -func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bool, userID, key string, withOwnerRemoved bool, queries ...SearchQuery) (_ *UserMetadata, err error) { +func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bool, userID, key string, withOwnerRemoved bool, queries ...SearchQuery) (metadata *UserMetadata, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -102,11 +102,14 @@ func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bo return nil, errors.ThrowInternal(err, "QUERY-aDGG2", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + metadata, err = scan(row) + return err + }, stmt, args...) + return metadata, err } -func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool, userID string, queries *UserMetadataSearchQueries, withOwnerRemoved bool) (_ *UserMetadataList, err error) { +func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool, userID string, queries *UserMetadataSearchQueries, withOwnerRemoved bool) (metadata *UserMetadataList, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -127,11 +130,10 @@ func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool return nil, errors.ThrowInternal(err, "QUERY-Egbgd", "Errors.Query.SQLStatment") } - rows, err := q.client.QueryContext(ctx, stmt, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-Hr2wf", "Errors.Internal") - } - metadata, err := scan(rows) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + metadata, err = scan(rows) + return err + }, stmt, args...) if err != nil { return nil, err } diff --git a/internal/query/user_metadata_test.go b/internal/query/user_metadata_test.go index 781caf732..62f7cfb6a 100644 --- a/internal/query/user_metadata_test.go +++ b/internal/query/user_metadata_test.go @@ -62,7 +62,7 @@ func Test_UserMetadataPrepares(t *testing.T) { name: "prepareUserMetadataQuery no result", prepare: prepareUserMetadataQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(userMetadataQuery), nil, nil, @@ -117,7 +117,7 @@ func Test_UserMetadataPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*UserMetadata)(nil), }, { name: "prepareUserMetadataListQuery no result", @@ -238,7 +238,7 @@ func Test_UserMetadataPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*UserMetadataList)(nil), }, } for _, tt := range tests { diff --git a/internal/query/user_personal_access_token.go b/internal/query/user_personal_access_token.go index 5558eed3c..1024fae5b 100644 --- a/internal/query/user_personal_access_token.go +++ b/internal/query/user_personal_access_token.go @@ -85,7 +85,7 @@ type PersonalAccessTokenSearchQueries struct { Queries []SearchQuery } -func (q *Queries) PersonalAccessTokenByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (_ *PersonalAccessToken, err error) { +func (q *Queries) PersonalAccessTokenByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (pat *PersonalAccessToken, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -109,8 +109,14 @@ func (q *Queries) PersonalAccessTokenByID(ctx context.Context, shouldTriggerBulk return nil, errors.ThrowInternal(err, "QUERY-Dgfb4", "Errors.Query.SQLStatment") } - row := q.client.QueryRowContext(ctx, stmt, args...) - return scan(row) + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + pat, err = scan(row) + return err + }, stmt, args...) + if err != nil { + return nil, err + } + return pat, nil } func (q *Queries) SearchPersonalAccessTokens(ctx context.Context, queries *PersonalAccessTokenSearchQueries, withOwnerRemoved bool) (personalAccessTokens *PersonalAccessTokens, err error) { @@ -129,14 +135,15 @@ func (q *Queries) SearchPersonalAccessTokens(ctx context.Context, queries *Perso return nil, errors.ThrowInvalidArgument(err, "QUERY-Hjw2w", "Errors.Query.InvalidRequest") } - rows, err := q.client.QueryContext(ctx, stmt, args...) + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + personalAccessTokens, err = scan(rows) + return err + + }, stmt, args...) if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Bmz63", "Errors.Internal") } - personalAccessTokens, err = scan(rows) - if err != nil { - return nil, err - } + personalAccessTokens.LatestSequence, err = q.latestSequence(ctx, personalAccessTokensTable) return personalAccessTokens, err } diff --git a/internal/query/user_personal_access_token_test.go b/internal/query/user_personal_access_token_test.go index 6a74e04de..c12a151ed 100644 --- a/internal/query/user_personal_access_token_test.go +++ b/internal/query/user_personal_access_token_test.go @@ -75,7 +75,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { name: "preparePersonalAccessTokenQuery no result", prepare: preparePersonalAccessTokenQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( personalAccessTokenStmt, nil, nil, @@ -134,7 +134,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PersonalAccessToken)(nil), }, { name: "preparePersonalAccessTokensQuery no result", @@ -261,7 +261,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*PersonalAccessTokens)(nil), }, } for _, tt := range tests { diff --git a/internal/query/user_test.go b/internal/query/user_test.go index d90e17b90..986d3c1e9 100644 --- a/internal/query/user_test.go +++ b/internal/query/user_test.go @@ -333,7 +333,7 @@ func Test_UserPrepares(t *testing.T) { name: "prepareUserQuery no result", prepare: prepareUserQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(userQuery), nil, nil, @@ -489,13 +489,13 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*User)(nil), }, { name: "prepareProfileQuery no result", prepare: prepareProfileQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(profileQuery), nil, nil, @@ -552,7 +552,7 @@ func Test_UserPrepares(t *testing.T) { name: "prepareProfileQuery not human found (error)", prepare: prepareProfileQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(profileQuery), profileCols, []driver.Value{ @@ -595,13 +595,13 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Profile)(nil), }, { name: "prepareEmailQuery no result", prepare: prepareEmailQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(emailQuery), nil, nil, @@ -650,7 +650,7 @@ func Test_UserPrepares(t *testing.T) { name: "prepareEmailQuery not human found (error)", prepare: prepareEmailQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(emailQuery), emailCols, []driver.Value{ @@ -689,13 +689,13 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Email)(nil), }, { name: "preparePhoneQuery no result", prepare: preparePhoneQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(phoneQuery), nil, nil, @@ -744,7 +744,7 @@ func Test_UserPrepares(t *testing.T) { name: "preparePhoneQuery not human found (error)", prepare: preparePhoneQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(phoneQuery), phoneCols, []driver.Value{ @@ -783,7 +783,7 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Phone)(nil), }, { name: "prepareUserUniqueQuery no result", @@ -837,13 +837,13 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: false, }, { name: "prepareNotifyUserQuery no result", prepare: prepareNotifyUserQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(notifyUserQuery), nil, nil, @@ -924,7 +924,7 @@ func Test_UserPrepares(t *testing.T) { name: "prepareNotifyUserQuery not notify found (error)", prepare: prepareNotifyUserQuery, want: want{ - sqlExpectations: mockQuery( + sqlExpectations: mockQueryScanErr( regexp.QuoteMeta(notifyUserQuery), notifyUserCols, []driver.Value{ @@ -980,13 +980,13 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*NotifyUser)(nil), }, { name: "prepareUsersQuery no result", prepare: prepareUsersQuery, want: want{ - sqlExpectations: mockQueries( + sqlExpectations: mockQuery( regexp.QuoteMeta(usersQuery), nil, nil, @@ -1214,7 +1214,7 @@ func Test_UserPrepares(t *testing.T) { return nil, true }, }, - object: nil, + object: (*Users)(nil), }, } for _, tt := range tests { diff --git a/internal/view/repository/db_mock_test.go b/internal/view/repository/db_mock_test.go index 5fc8e6677..7abbabd98 100644 --- a/internal/view/repository/db_mock_test.go +++ b/internal/view/repository/db_mock_test.go @@ -175,48 +175,58 @@ func (db *dbMock) expectRollback(err error) *dbMock { func (db *dbMock) expectGetByID(table, key, value string) *dbMock { query := fmt.Sprintf(expectedGetByID, table, key) + db.mock.ExpectBegin() db.mock.ExpectQuery(query). WithArgs(value). WillReturnRows(sqlmock.NewRows([]string{key}). AddRow(key)) + db.mock.ExpectCommit() return db } func (db *dbMock) expectGetByIDErr(table, key, value string, err error) *dbMock { query := fmt.Sprintf(expectedGetByID, table, key) + db.mock.ExpectBegin() db.mock.ExpectQuery(query). WithArgs(value). WillReturnError(err) + db.mock.ExpectCommit() return db } func (db *dbMock) expectGetByQuery(table, key, method, value string) *dbMock { query := fmt.Sprintf(expectedGetByQuery, table, key, method) + db.mock.ExpectBegin() db.mock.ExpectQuery(query). WithArgs(value). WillReturnRows(sqlmock.NewRows([]string{key}). AddRow(key)) + db.mock.ExpectCommit() return db } func (db *dbMock) expectGetByQueryCaseSensitive(table, key, method, value string) *dbMock { query := fmt.Sprintf(expectedGetByQueryCaseSensitive, table, key, method) + db.mock.ExpectBegin() db.mock.ExpectQuery(query). WithArgs(value). WillReturnRows(sqlmock.NewRows([]string{key}). AddRow(key)) + db.mock.ExpectCommit() return db } func (db *dbMock) expectGetByQueryErr(table, key, method, value string, err error) *dbMock { query := fmt.Sprintf(expectedGetByQuery, table, key, method) + db.mock.ExpectBegin() db.mock.ExpectQuery(query). WithArgs(value). WillReturnError(err) + db.mock.ExpectCommit() return db } @@ -313,10 +323,14 @@ func (db *dbMock) expectGetSearchRequestNoParams(table string, resultAmount, tot rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() + db.mock.ExpectQuery(queryCount). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WillReturnRows(rows) + + db.mock.ExpectCommit() return db } @@ -329,10 +343,12 @@ func (db *dbMock) expectGetSearchRequestWithLimit(table string, limit, resultAmo rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -345,10 +361,12 @@ func (db *dbMock) expectGetSearchRequestWithOffset(table string, offset, resultA rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -361,10 +379,12 @@ func (db *dbMock) expectGetSearchRequestWithSorting(table, sorting string, sorti rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -377,12 +397,14 @@ func (db *dbMock) expectGetSearchRequestWithSearchQuery(table, key, method, valu rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WithArgs(value). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WithArgs(value). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -395,12 +417,14 @@ func (db *dbMock) expectGetSearchRequestWithAllParams(table, key, method, value, rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WithArgs(value). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WithArgs(value). WillReturnRows(rows) + db.mock.ExpectCommit() return db } @@ -413,9 +437,11 @@ func (db *dbMock) expectGetSearchRequestErr(table string, resultAmount, total in rows.AddRow(fmt.Sprintf("hodor-%d", i)) } + db.mock.ExpectBegin() db.mock.ExpectQuery(queryCount). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(total)) db.mock.ExpectQuery(query). WillReturnError(err) + db.mock.ExpectCommit() return db } diff --git a/internal/view/repository/query.go b/internal/view/repository/query.go index 36d41142a..77d27c75b 100644 --- a/internal/view/repository/query.go +++ b/internal/view/repository/query.go @@ -1,10 +1,14 @@ package repository import ( + "context" + "database/sql" "fmt" "github.com/jinzhu/gorm" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -48,6 +52,13 @@ func PrepareSearchQuery(table string, request SearchRequest) func(db *gorm.DB, r } } + query = query.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}) + defer func() { + if err := query.Commit().Error; err != nil { + logging.OnError(err).Info("commit failed") + } + }() + query = query.Count(&count) if res == nil { return count, nil diff --git a/internal/view/repository/requests.go b/internal/view/repository/requests.go index a624643c4..74921fbcb 100644 --- a/internal/view/repository/requests.go +++ b/internal/view/repository/requests.go @@ -1,6 +1,8 @@ package repository import ( + "context" + "database/sql" "errors" "fmt" "strings" @@ -13,7 +15,14 @@ import ( func PrepareGetByKey(table string, key ColumnKey, id string) func(db *gorm.DB, res interface{}) error { return func(db *gorm.DB, res interface{}) error { - err := db.Table(table). + tx := db.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}) + defer func() { + if err := tx.Commit().Error; err != nil { + logging.OnError(err).Info("commit failed") + } + }() + + err := tx.Table(table). Where(fmt.Sprintf("%s = ?", key.ToColumnName()), id). Take(res). Error @@ -39,7 +48,14 @@ func PrepareGetByQuery(table string, queries ...SearchQuery) func(db *gorm.DB, r } } - err := query.Take(res).Error + tx := query.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}) + defer func() { + if err := tx.Commit().Error; err != nil { + logging.OnError(err).Info("commit failed") + } + }() + + err := tx.Take(res).Error if err == nil { return nil } From 85194666ffbe2b3e0b98794a35a2ca406184de39 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 23 Aug 2023 10:04:29 +0200 Subject: [PATCH 54/76] fix: use system secret config if generator type does not exist on instance (#6420) * fix: use system secret config if generator type does not exist on instance * remove unused idGenerator (cherry picked from commit cbd2ef0612a65720eed36a3843c1e7003b5d74a0) --- cmd/setup/03.go | 1 + cmd/setup/config_change.go | 1 + cmd/start/start.go | 1 + internal/command/command.go | 21 ++-- internal/command/crypto.go | 23 ++++ internal/command/instance.go | 37 +++--- internal/command/user_human_otp.go | 5 +- internal/command/user_human_otp_test.go | 148 +++++++++++++++++++++--- 8 files changed, 195 insertions(+), 42 deletions(-) diff --git a/cmd/setup/03.go b/cmd/setup/03.go index 73d32fcab..ffa4769a4 100644 --- a/cmd/setup/03.go +++ b/cmd/setup/03.go @@ -91,6 +91,7 @@ func (mig *FirstInstance) Execute(ctx context.Context) error { 0, 0, 0, + nil, ) if err != nil { return err diff --git a/cmd/setup/config_change.go b/cmd/setup/config_change.go index 28d66394c..c6f107fcf 100644 --- a/cmd/setup/config_change.go +++ b/cmd/setup/config_change.go @@ -57,6 +57,7 @@ func (mig *externalConfigChange) Execute(ctx context.Context) error { 0, 0, 0, + nil, ) if err != nil { diff --git a/cmd/start/start.go b/cmd/start/start.go index c8a5c20e3..1191e19b2 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -202,6 +202,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error config.OIDC.DefaultAccessTokenLifetime, config.OIDC.DefaultRefreshTokenExpiration, config.OIDC.DefaultRefreshTokenIdleExpiration, + config.DefaultInstance.SecretGenerators, ) if err != nil { return fmt.Errorf("cannot start commands: %w", err) diff --git a/internal/command/command.go b/internal/command/command.go index 9a6a87cb7..514965273 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -62,15 +62,16 @@ type Commands struct { defaultRefreshTokenLifetime time.Duration defaultRefreshTokenIdleLifetime time.Duration - multifactors domain.MultifactorConfigs - webauthnConfig *webauthn_helper.Config - keySize int - keyAlgorithm crypto.EncryptionAlgorithm - certificateAlgorithm crypto.EncryptionAlgorithm - certKeySize int - privateKeyLifetime time.Duration - publicKeyLifetime time.Duration - certificateLifetime time.Duration + multifactors domain.MultifactorConfigs + webauthnConfig *webauthn_helper.Config + keySize int + keyAlgorithm crypto.EncryptionAlgorithm + certificateAlgorithm crypto.EncryptionAlgorithm + certKeySize int + privateKeyLifetime time.Duration + publicKeyLifetime time.Duration + certificateLifetime time.Duration + defaultSecretGenerators *SecretGenerators } func StartCommands( @@ -89,6 +90,7 @@ func StartCommands( defaultAccessTokenLifetime, defaultRefreshTokenLifetime, defaultRefreshTokenIdleLifetime time.Duration, + defaultSecretGenerators *SecretGenerators, ) (repo *Commands, err error) { if externalDomain == "" { return nil, errors.ThrowInvalidArgument(nil, "COMMAND-Df21s", "no external domain specified") @@ -125,6 +127,7 @@ func StartCommands( defaultAccessTokenLifetime: defaultAccessTokenLifetime, defaultRefreshTokenLifetime: defaultRefreshTokenLifetime, defaultRefreshTokenIdleLifetime: defaultRefreshTokenIdleLifetime, + defaultSecretGenerators: defaultSecretGenerators, } instance_repo.RegisterEventMappers(repo.eventstore) diff --git a/internal/command/crypto.go b/internal/command/crypto.go index 8d145f2cc..af9407e0a 100644 --- a/internal/command/crypto.go +++ b/internal/command/crypto.go @@ -76,3 +76,26 @@ func secretGeneratorConfig(ctx context.Context, filter preparation.FilterToQuery IncludeSymbols: wm.IncludeSymbols, }, nil } + +func secretGeneratorConfigWithDefault(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, defaultGenerator *crypto.GeneratorConfig) (*crypto.GeneratorConfig, error) { + wm := NewInstanceSecretGeneratorConfigWriteModel(ctx, typ) + events, err := filter(ctx, wm.Query()) + if err != nil { + return nil, err + } + wm.AppendEvents(events...) + if err := wm.Reduce(); err != nil { + return nil, err + } + if wm.State != domain.SecretGeneratorStateActive { + return defaultGenerator, nil + } + return &crypto.GeneratorConfig{ + Length: wm.Length, + Expiry: wm.Expiry, + IncludeLowerLetters: wm.IncludeLowerLetters, + IncludeUpperLetters: wm.IncludeUpperLetters, + IncludeDigits: wm.IncludeDigits, + IncludeSymbols: wm.IncludeSymbols, + }, nil +} diff --git a/internal/command/instance.go b/internal/command/instance.go index e0a471fe9..40c035d3e 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -33,24 +33,12 @@ const ( ) type InstanceSetup struct { - zitadel ZitadelConfig - idGenerator id.Generator - InstanceName string - CustomDomain string - DefaultLanguage language.Tag - Org InstanceOrgSetup - SecretGenerators struct { - PasswordSaltCost uint - ClientSecret *crypto.GeneratorConfig - InitializeUserCode *crypto.GeneratorConfig - EmailVerificationCode *crypto.GeneratorConfig - PhoneVerificationCode *crypto.GeneratorConfig - PasswordVerificationCode *crypto.GeneratorConfig - PasswordlessInitCode *crypto.GeneratorConfig - DomainVerification *crypto.GeneratorConfig - OTPSMS *crypto.GeneratorConfig - OTPEmail *crypto.GeneratorConfig - } + zitadel ZitadelConfig + InstanceName string + CustomDomain string + DefaultLanguage language.Tag + Org InstanceOrgSetup + SecretGenerators *SecretGenerators PasswordComplexityPolicy struct { MinLength uint64 HasLowercase bool @@ -126,6 +114,19 @@ type InstanceSetup struct { } } +type SecretGenerators struct { + PasswordSaltCost uint + ClientSecret *crypto.GeneratorConfig + InitializeUserCode *crypto.GeneratorConfig + EmailVerificationCode *crypto.GeneratorConfig + PhoneVerificationCode *crypto.GeneratorConfig + PasswordVerificationCode *crypto.GeneratorConfig + PasswordlessInitCode *crypto.GeneratorConfig + DomainVerification *crypto.GeneratorConfig + OTPSMS *crypto.GeneratorConfig + OTPEmail *crypto.GeneratorConfig +} + type ZitadelConfig struct { projectID string mgmtAppID string diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index c3a8e84f0..170dde17a 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -278,6 +278,7 @@ func (c *Commands) HumanSendOTPSMS(ctx context.Context, userID, resourceOwner st authRequest, smsWriteModel, domain.SecretGeneratorTypeOTPSMS, + c.defaultSecretGenerators.OTPSMS, codeAddedEvent, ) } @@ -398,6 +399,7 @@ func (c *Commands) HumanSendOTPEmail(ctx context.Context, userID, resourceOwner authRequest, smsWriteModel, domain.SecretGeneratorTypeOTPEmail, + c.defaultSecretGenerators.OTPEmail, codeAddedEvent, ) } @@ -442,6 +444,7 @@ func (c *Commands) sendHumanOTP( authRequest *domain.AuthRequest, writeModelByID func(ctx context.Context, userID string, resourceOwner string) (OTPWriteModel, error), secretGeneratorType domain.SecretGeneratorType, + defaultSecretGenerator *crypto.GeneratorConfig, codeAddedEvent func(ctx context.Context, aggregate *eventstore.Aggregate, code *crypto.CryptoValue, expiry time.Duration, info *user.AuthRequestInfo) eventstore.Command, ) (err error) { if userID == "" { @@ -454,7 +457,7 @@ func (c *Commands) sendHumanOTP( if !existingOTP.OTPAdded() { return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-SFD52", "Errors.User.MFA.OTP.NotReady") } - config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, secretGeneratorType) + config, err := secretGeneratorConfigWithDefault(ctx, c.eventstore.Filter, secretGeneratorType, defaultSecretGenerator) if err != nil { return err } diff --git a/internal/command/user_human_otp_test.go b/internal/command/user_human_otp_test.go index 5ec52582a..ba3a6dc7b 100644 --- a/internal/command/user_human_otp_test.go +++ b/internal/command/user_human_otp_test.go @@ -1249,9 +1249,20 @@ func TestCommandSide_RemoveHumanOTPSMS(t *testing.T) { func TestCommandSide_HumanSendOTPSMS(t *testing.T) { ctx := authz.NewMockContext("inst1", "org1", "user1") + defaultGenerators := &SecretGenerators{ + OTPSMS: &crypto.GeneratorConfig{ + Length: 8, + Expiry: time.Hour, + IncludeLowerLetters: true, + IncludeUpperLetters: true, + IncludeDigits: true, + IncludeSymbols: true, + }, + } type fields struct { - eventstore func(*testing.T) *eventstore.Eventstore - userEncryption crypto.EncryptionAlgorithm + eventstore func(*testing.T) *eventstore.Eventstore + userEncryption crypto.EncryptionAlgorithm + defaultSecretGenerators *SecretGenerators } type ( args struct { @@ -1274,7 +1285,8 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: expectEventstore(), + eventstore: expectEventstore(), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -1291,6 +1303,7 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { eventstore: expectEventstore( expectFilter(), ), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -1343,7 +1356,52 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { }, ), ), - userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, + }, + args: args{ + ctx: ctx, + userID: "user1", + resourceOwner: "org1", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, + { + name: "successful add (without secret config)", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanOTPSMSAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + ), + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID("inst1", + user.NewHumanOTPSMSCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), + ), + }, + ), + ), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -1406,7 +1464,8 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { }, ), ), - userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -1432,8 +1491,9 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore(t), - userEncryption: tt.fields.userEncryption, + eventstore: tt.fields.eventstore(t), + userEncryption: tt.fields.userEncryption, + defaultSecretGenerators: tt.fields.defaultSecretGenerators, } err := r.HumanSendOTPSMS(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authRequest) assert.ErrorIs(t, err, tt.res.err) @@ -2176,9 +2236,20 @@ func TestCommandSide_RemoveHumanOTPEmail(t *testing.T) { func TestCommandSide_HumanSendOTPEmail(t *testing.T) { ctx := authz.NewMockContext("inst1", "org1", "user1") + defaultGenerators := &SecretGenerators{ + OTPEmail: &crypto.GeneratorConfig{ + Length: 8, + Expiry: time.Hour, + IncludeLowerLetters: true, + IncludeUpperLetters: true, + IncludeDigits: true, + IncludeSymbols: true, + }, + } type fields struct { - eventstore func(*testing.T) *eventstore.Eventstore - userEncryption crypto.EncryptionAlgorithm + eventstore func(*testing.T) *eventstore.Eventstore + userEncryption crypto.EncryptionAlgorithm + defaultSecretGenerators *SecretGenerators } type ( args struct { @@ -2201,7 +2272,8 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: expectEventstore(), + eventstore: expectEventstore(), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -2218,6 +2290,7 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { eventstore: expectEventstore( expectFilter(), ), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -2270,7 +2343,52 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { }, ), ), - userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, + }, + args: args{ + ctx: ctx, + userID: "user1", + resourceOwner: "org1", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, + { + name: "successful add (without secret config)", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanOTPEmailAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + ), + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID("inst1", + user.NewHumanOTPEmailCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), + ), + }, + ), + ), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -2333,7 +2451,8 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { }, ), ), - userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), + defaultSecretGenerators: defaultGenerators, }, args: args{ ctx: ctx, @@ -2359,8 +2478,9 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore(t), - userEncryption: tt.fields.userEncryption, + eventstore: tt.fields.eventstore(t), + userEncryption: tt.fields.userEncryption, + defaultSecretGenerators: tt.fields.defaultSecretGenerators, } err := r.HumanSendOTPEmail(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.authRequest) assert.ErrorIs(t, err, tt.res.err) From a628f2222e00e910f269530f06fee5afc0066693 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 23 Aug 2023 13:55:52 +0200 Subject: [PATCH 55/76] fix: restrict AllowRegistration check to local registration (#5939) * fix: restrict AllowRegistration check to local registration * add comment * add additional tests * hide registration fields if no registration allowed * fix: always allow linking and creation of external idps on users in userV2 and admin import * chore: exclude console dist and node_module folders from cache * chore: include node_module folders into cache again * linting (cherry picked from commit 66772ad0dd4746fbe9ddabb4cdd254ac5fb03028) --- internal/api/grpc/admin/import.go | 9 +- internal/api/grpc/user/v2/user.go | 8 +- .../api/ui/login/external_provider_handler.go | 2 + .../templates/external_not_found_option.html | 9 +- internal/command/idp_model.go | 51 +++++ internal/command/user_human.go | 5 +- internal/command/user_human_test.go | 191 ++++++++++++++++ internal/command/user_idp_link.go | 25 ++- internal/command/user_idp_link_test.go | 208 ++++++++++++++++-- 9 files changed, 468 insertions(+), 40 deletions(-) diff --git a/internal/api/grpc/admin/import.go b/internal/api/grpc/admin/import.go index b5efbb781..6b1e54fef 100644 --- a/internal/api/grpc/admin/import.go +++ b/internal/api/grpc/admin/import.go @@ -623,11 +623,10 @@ func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*adm if org.UserLinks != nil { for _, userLinks := range org.GetUserLinks() { logging.Debugf("import userlink: %s", userLinks.GetUserId()+"_"+userLinks.GetIdpId()+"_"+userLinks.GetProvidedUserId()+"_"+userLinks.GetProvidedUserName()) - externalIDP := &domain.UserIDPLink{ - ObjectRoot: models.ObjectRoot{AggregateID: userLinks.UserId}, - IDPConfigID: userLinks.IdpId, - ExternalUserID: userLinks.ProvidedUserId, - DisplayName: userLinks.ProvidedUserName, + externalIDP := &command.AddLink{ + IDPID: userLinks.IdpId, + IDPExternalID: userLinks.ProvidedUserId, + DisplayName: userLinks.ProvidedUserName, } if _, err := s.command.AddUserIDPLink(ctx, userLinks.UserId, org.GetOrgId(), externalIDP); err != nil { errors = append(errors, &admin_pb.ImportDataError{Type: "user_link", Id: userLinks.UserId + "_" + userLinks.IdpId, Message: err.Error()}) diff --git a/internal/api/grpc/user/v2/user.go b/internal/api/grpc/user/v2/user.go index f4ec0eaa9..78df98a90 100644 --- a/internal/api/grpc/user/v2/user.go +++ b/internal/api/grpc/user/v2/user.go @@ -116,10 +116,10 @@ func genderToDomain(gender user.Gender) domain.Gender { func (s *Server) AddIDPLink(ctx context.Context, req *user.AddIDPLinkRequest) (_ *user.AddIDPLinkResponse, err error) { orgID := authz.GetCtxData(ctx).OrgID - details, err := s.command.AddUserIDPLink(ctx, req.UserId, orgID, &domain.UserIDPLink{ - IDPConfigID: req.GetIdpLink().GetIdpId(), - ExternalUserID: req.GetIdpLink().GetUserId(), - DisplayName: req.GetIdpLink().GetUserName(), + details, err := s.command.AddUserIDPLink(ctx, req.UserId, orgID, &command.AddLink{ + IDPID: req.GetIdpLink().GetIdpId(), + DisplayName: req.GetIdpLink().GetUserName(), + IDPExternalID: req.GetIdpLink().GetUserId(), }) if err != nil { return nil, err diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index d895f1aef..f9bfb4023 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -66,6 +66,7 @@ type externalNotFoundOptionData struct { ExternalEmailVerified bool ExternalPhone domain.PhoneNumber ExternalPhoneVerified bool + ProviderName string } type externalRegisterFormData struct { @@ -503,6 +504,7 @@ func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Requ ShowUsername: orgIAMPolicy.UserLoginMustBeDomain, ShowUsernameSuffix: !labelPolicy.HideLoginNameSuffix, OrgRegister: orgIAMPolicy.UserLoginMustBeDomain, + ProviderName: domain.IDPName(idpTemplate.Name, idpTemplate.Type), } if human.Phone != nil { data.Phone = human.PhoneNumber diff --git a/internal/api/ui/login/static/templates/external_not_found_option.html b/internal/api/ui/login/static/templates/external_not_found_option.html index 086662a8e..dcc1961b4 100644 --- a/internal/api/ui/login/static/templates/external_not_found_option.html +++ b/internal/api/ui/login/static/templates/external_not_found_option.html @@ -1,8 +1,11 @@ {{template "main-top" .}}

@@ -21,6 +24,7 @@
+ {{ if or .IsCreationAllowed }}
@@ -85,8 +89,9 @@
+ {{end}} - {{ if or .TOSLink .PrivacyLink }} + {{ if and (or .IsLinkingAllowed .IsCreationAllowed) (or .TOSLink .PrivacyLink) }}
{{ if .TOSLink }} diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 417f98464..e01d83124 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -189,6 +189,10 @@ func (wm *OAuthIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encry ) } +func (wm *OAuthIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type OIDCIDPWriteModel struct { eventstore.WriteModel @@ -310,7 +314,10 @@ func (wm *OIDCIDPWriteModel) NewChanges( // reduceIDPConfigAddedEvent handles old idpConfig events func (wm *OIDCIDPWriteModel) reduceIDPConfigAddedEvent(e *idpconfig.IDPConfigAddedEvent) { wm.Name = e.Name + wm.Options.IsCreationAllowed = true + wm.Options.IsLinkingAllowed = true wm.Options.IsAutoCreation = e.AutoRegister + wm.Options.IsAutoUpdate = false wm.State = domain.IDPStateActive } @@ -382,6 +389,10 @@ func (wm *OIDCIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encryp ) } +func (wm *OIDCIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type JWTIDPWriteModel struct { eventstore.WriteModel @@ -483,7 +494,10 @@ func (wm *JWTIDPWriteModel) NewChanges( // reduceIDPConfigAddedEvent handles old idpConfig events func (wm *JWTIDPWriteModel) reduceIDPConfigAddedEvent(e *idpconfig.IDPConfigAddedEvent) { wm.Name = e.Name + wm.Options.IsCreationAllowed = true + wm.Options.IsLinkingAllowed = true wm.Options.IsAutoCreation = e.AutoRegister + wm.Options.IsAutoUpdate = false wm.State = domain.IDPStateActive } @@ -546,6 +560,10 @@ func (wm *JWTIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encrypt ) } +func (wm *JWTIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type AzureADIDPWriteModel struct { eventstore.WriteModel @@ -690,6 +708,10 @@ func (wm *AzureADIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Enc ) } +func (wm *AzureADIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type GitHubIDPWriteModel struct { eventstore.WriteModel @@ -803,6 +825,10 @@ func (wm *GitHubIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encr ) } +func (wm *GitHubIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type GitHubEnterpriseIDPWriteModel struct { eventstore.WriteModel @@ -947,6 +973,10 @@ func (wm *GitHubEnterpriseIDPWriteModel) ToProvider(callbackURL string, idpAlg c ) } +func (wm *GitHubEnterpriseIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type GitLabIDPWriteModel struct { eventstore.WriteModel @@ -1061,6 +1091,10 @@ func (wm *GitLabIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encr ) } +func (wm *GitLabIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type GitLabSelfHostedIDPWriteModel struct { eventstore.WriteModel @@ -1185,6 +1219,10 @@ func (wm *GitLabSelfHostedIDPWriteModel) ToProvider(callbackURL string, idpAlg c ) } +func (wm *GitLabSelfHostedIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type GoogleIDPWriteModel struct { eventstore.WriteModel @@ -1306,6 +1344,10 @@ func (wm *GoogleIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encr ) } +func (wm *GoogleIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type LDAPIDPWriteModel struct { eventstore.WriteModel @@ -1541,6 +1583,10 @@ func (wm *LDAPIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encryp ), nil } +func (wm *LDAPIDPWriteModel) GetProviderOptions() idp.Options { + return wm.Options +} + type IDPRemoveWriteModel struct { eventstore.WriteModel @@ -1771,6 +1817,7 @@ func (wm *IDPTypeWriteModel) Query() *eventstore.SearchQueryBuilder { type IDP interface { eventstore.QueryReducer ToProvider(string, crypto.EncryptionAlgorithm) (providers.Provider, error) + GetProviderOptions() idp.Options } type AllIDPWriteModel struct { @@ -1863,3 +1910,7 @@ func (wm *AllIDPWriteModel) AppendEvents(events ...eventstore.Event) { func (wm *AllIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.EncryptionAlgorithm) (providers.Provider, error) { return wm.model.ToProvider(callbackURL, idpAlg) } + +func (wm *AllIDPWriteModel) GetProviderOptions() idp.Options { + return wm.model.GetProviderOptions() +} diff --git a/internal/command/user_human.go b/internal/command/user_human.go index 044066aba..645d6bf9e 100644 --- a/internal/command/user_human.go +++ b/internal/command/user_human.go @@ -475,7 +475,8 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai if err != nil { return nil, errors.ThrowPreconditionFailed(err, "COMMAND-Dfg3g", "Errors.Org.LoginPolicy.NotFound") } - if !loginPolicy.AllowRegister { + // check only if local registration is allowed, the idp will be checked separately + if !loginPolicy.AllowRegister && link == nil { return nil, errors.ThrowPreconditionFailed(err, "COMMAND-SAbr3", "Errors.Org.LoginPolicy.RegistrationNotAllowed") } userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) @@ -605,7 +606,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain. } for _, link := range links { - event, err := c.addUserIDPLink(ctx, userAgg, link) + event, err := c.addUserIDPLink(ctx, userAgg, link, false) if err != nil { return nil, nil, err } diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go index d6964ac90..4b02e6e44 100644 --- a/internal/command/user_human_test.go +++ b/internal/command/user_human_test.go @@ -20,6 +20,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -2000,6 +2001,31 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idpID", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + false, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "idpID", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + ), expectFilter( eventFromEventPusher( org.NewIDPConfigAddedEvent(context.Background(), @@ -2102,6 +2128,146 @@ func TestCommandSide_ImportHuman(t *testing.T) { }, }, }, + { + name: "add human (with idp, creation not allowed), precondition error", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + org.NewDomainPolicyAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + true, + true, + true, + ), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewPasswordComplexityPolicyAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + 1, + false, + false, + false, + false, + ), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idpID", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + false, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "idpID", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + eventFromEventPusher( + func() eventstore.Command { + e, _ := org.NewOIDCIDPChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + []idp.OIDCIDPChanges{ + idp.ChangeOIDCOptions(idp.OptionChanges{IsCreationAllowed: gu.Ptr(false)}), + }, + ) + return e + }(), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idpID", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + false, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "idpID", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + eventFromEventPusher( + func() eventstore.Command { + e, _ := org.NewOIDCIDPChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + []idp.OIDCIDPChanges{ + idp.ChangeOIDCOptions(idp.OptionChanges{IsCreationAllowed: gu.Ptr(false)}), + }, + ) + return e + }(), + ), + eventFromEventPusher( + org.NewIdentityProviderAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idpID", + domain.IdentityProviderTypeOrg, + ), + ), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), + userPasswordHasher: mockPasswordHasher("x"), + }, + args: args{ + ctx: context.Background(), + orgID: "org1", + human: &domain.Human{ + Username: "username", + Profile: &domain.Profile{ + FirstName: "firstname", + LastName: "lastname", + PreferredLanguage: language.English, + }, + Email: &domain.Email{ + EmailAddress: "email@test.ch", + IsEmailVerified: true, + }, + }, + links: []*domain.UserIDPLink{ + { + IDPConfigID: "idpID", + ExternalUserID: "externalID", + DisplayName: "name", + }, + }, + secretGenerator: GetMockSecretGenerator(t), + }, + res: res{ + err: caos_errs.IsPreconditionFailed, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -3333,6 +3499,31 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idpID", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + false, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "idpID", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + ), expectPush( []*repository.Event{ eventFromEventPusher( diff --git a/internal/command/user_idp_link.go b/internal/command/user_idp_link.go index 46dd96b40..bf7b6c72c 100644 --- a/internal/command/user_idp_link.go +++ b/internal/command/user_idp_link.go @@ -11,7 +11,7 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner string, link *domain.UserIDPLink) (_ *domain.ObjectDetails, err error) { +func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner string, link *AddLink) (_ *domain.ObjectDetails, err error) { if userID == "" { return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing") } @@ -23,11 +23,7 @@ func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner str return nil, err } } - - linkWriteModel := NewUserIDPLinkWriteModel(userID, link.IDPConfigID, link.ExternalUserID, resourceOwner) - userAgg := UserAggregateFromWriteModel(&linkWriteModel.WriteModel) - - event, err := c.addUserIDPLink(ctx, userAgg, link) + event, err := addLink(ctx, c.eventstore.Filter, user.NewAggregate(userID, resourceOwner), link) if err != nil { return nil, err } @@ -60,7 +56,7 @@ func (c *Commands) BulkAddedUserIDPLinks(ctx context.Context, userID, resourceOw linkWriteModel := NewUserIDPLinkWriteModel(userID, link.IDPConfigID, link.ExternalUserID, resourceOwner) userAgg := UserAggregateFromWriteModel(&linkWriteModel.WriteModel) - events[i], err = c.addUserIDPLink(ctx, userAgg, link) + events[i], err = c.addUserIDPLink(ctx, userAgg, link, true) if err != nil { return err } @@ -70,18 +66,25 @@ func (c *Commands) BulkAddedUserIDPLinks(ctx context.Context, userID, resourceOw return err } -func (c *Commands) addUserIDPLink(ctx context.Context, human *eventstore.Aggregate, link *domain.UserIDPLink) (eventstore.Command, error) { +func (c *Commands) addUserIDPLink(ctx context.Context, human *eventstore.Aggregate, link *domain.UserIDPLink, linkToExistingUser bool) (eventstore.Command, error) { if link.AggregateID != "" && human.ID != link.AggregateID { return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-33M0g", "Errors.IDMissing") } if !link.IsValid() { return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid") } - - exists, err := ExistsIDP(ctx, c.eventstore.Filter, link.IDPConfigID, human.ResourceOwner) - if !exists || err != nil { + idpWriteModel, err := IDPProviderWriteModel(ctx, c.eventstore.Filter, link.IDPConfigID) + if err != nil { return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-39nfs", "Errors.IDPConfig.NotExisting") } + // IDP user will either be linked or created on a new user + // Therefore we need to either check if linking is allowed or creation: + if linkToExistingUser && !idpWriteModel.GetProviderOptions().IsLinkingAllowed { + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-Sfee2", "Errors.ExternalIDP.LinkingNotAllowed") + } + if !linkToExistingUser && !idpWriteModel.GetProviderOptions().IsCreationAllowed { + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-SJI3g", "Errors.ExternalIDP.CreationNotAllowed") + } return user.NewUserIDPLinkAddedEvent(ctx, human, link.IDPConfigID, link.DisplayName, link.ExternalUserID), nil } diff --git a/internal/command/user_idp_link_test.go b/internal/command/user_idp_link_test.go index 207a8e9df..68d7611c5 100644 --- a/internal/command/user_idp_link_test.go +++ b/internal/command/user_idp_link_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "golang.org/x/text/language" @@ -12,6 +13,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -28,7 +30,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { links []*domain.UserIDPLink } type res struct { - err func(error) bool + err error } tests := []struct { name string @@ -55,7 +57,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { resourceOwner: "org1", }, res: res{ - err: caos_errs.IsErrorInvalidArgument, + err: caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing"), }, }, { @@ -71,7 +73,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { resourceOwner: "org1", }, res: res{ - err: caos_errs.IsErrorInvalidArgument, + err: caos_errs.ThrowInvalidArgument(nil, "COMMAND-Ek9s", "Errors.User.ExternalIDP.MinimumExternalIDPNeeded"), }, }, { @@ -113,7 +115,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { }, }, res: res{ - err: caos_errs.IsErrorInvalidArgument, + err: caos_errs.ThrowInvalidArgument(nil, "COMMAND-33M0g", "Errors.IDMissing"), }, }, { @@ -155,7 +157,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { }, }, res: res{ - err: caos_errs.IsErrorInvalidArgument, + err: caos_errs.ThrowInvalidArgument(nil, "COMMAND-6m9Kd", "Errors.User.ExternalIDP.Invalid"), }, }, { @@ -181,7 +183,6 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { ), ), expectFilter(), - expectFilter(), ), }, args: args{ @@ -199,7 +200,112 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { }, }, res: res{ - err: caos_errs.IsPreconditionFailed, + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-as02jin", "Errors.IDPConfig.NotExisting"), + }, + }, + { + name: "linking not allowed, precondition error", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent( + context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "userName", + "firstName", + "lastName", + "nickName", + "displayName", + language.German, + domain.GenderFemale, + "email@Address.ch", + false, + ), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + true, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + true, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + eventFromEventPusher( + func() eventstore.Command { + e, _ := org.NewOIDCIDPChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + []idp.OIDCIDPChanges{ + idp.ChangeOIDCOptions(idp.OptionChanges{IsLinkingAllowed: gu.Ptr(false)}), + }, + ) + return e + }(), + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + resourceOwner: "org1", + links: []*domain.UserIDPLink{ + { + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + }, + IDPConfigID: "config1", + DisplayName: "name", + ExternalUserID: "externaluser1", + }, + }, + }, + res: res{ + err: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Sfee2", "Errors.ExternalIDP.LinkingNotAllowed"), }, }, { @@ -235,6 +341,44 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { true, ), ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + ), + expectFilter( + eventFromEventPusher( + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + true, + ), + ), + eventFromEventPusher( + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), ), expectPush( []*repository.Event{ @@ -290,11 +434,10 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { ), ), ), - expectFilter(), expectFilter( - eventFromEventPusher( + eventFromEventPusherWithInstanceID("instance1", instance.NewIDPConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, + &instance.NewAggregate("instance1").Aggregate, "config1", "name", domain.IDPConfigTypeOIDC, @@ -302,6 +445,44 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { true, ), ), + eventFromEventPusherWithInstanceID("instance1", + instance.NewIDPOIDCConfigAddedEvent(context.Background(), + &instance.NewAggregate("instance1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), + ), + expectFilter( + eventFromEventPusherWithInstanceID("instance1", + instance.NewIDPConfigAddedEvent(context.Background(), + &instance.NewAggregate("instance1").Aggregate, + "config1", + "name", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeUnspecified, + true, + ), + ), + eventFromEventPusherWithInstanceID("instance1", + instance.NewIDPOIDCConfigAddedEvent(context.Background(), + &instance.NewAggregate("instance1").Aggregate, + "clientID", + "config1", + "issuer", + "authEndpoint", + "tokenEndpoint", + nil, + domain.OIDCMappingFieldUnspecified, + domain.OIDCMappingFieldUnspecified, + ), + ), ), expectPush( []*repository.Event{ @@ -342,12 +523,7 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { eventstore: tt.fields.eventstore, } err := r.BulkAddedUserIDPLinks(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.links) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } + assert.ErrorIs(t, err, tt.res.err) }) } } From ccb92b3c8dabba4c657af276e361eb48a552e5e9 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 22 Aug 2023 12:05:45 +0200 Subject: [PATCH 56/76] fix(api): naming cleanup in user and session service (#6379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(api): rename first and last name to given and family name, intent to idp_intent, remove _ actions * fix merge * fully rename intent to idp intent in api --------- Co-authored-by: Tim Möhlmann (cherry picked from commit a9fb2a6e5c0dc9ba2557529ba1181cd07a66afce) --- README.md | 4 +- .../integrate/login-ui/_select-account.mdx | 4 +- .../integrate/login-ui/external-login.mdx | 25 ++--- docs/docs/guides/integrate/login-ui/mfa.mdx | 4 +- .../integrate/login-ui/username-password.mdx | 4 +- .../api/grpc/org/v2/org_integration_test.go | 8 +- internal/api/grpc/org/v2/org_test.go | 4 +- internal/api/grpc/session/v2/session.go | 4 +- .../session/v2/session_integration_test.go | 24 ++--- internal/api/grpc/user/v2/user.go | 36 +++---- .../api/grpc/user/v2/user_integration_test.go | 94 +++++++++---------- internal/api/grpc/user/v2/user_test.go | 10 +- internal/integration/client.go | 4 +- .../session/v2alpha/session_service.proto | 16 ++-- proto/zitadel/user/v2alpha/idp.proto | 10 +- proto/zitadel/user/v2alpha/user.proto | 4 +- proto/zitadel/user/v2alpha/user_service.proto | 36 +++---- 17 files changed, 146 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index d92927437..14f4dd556 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ Do you look for a user management that's quickly set up like Auth0 and open source like Keycloak? -Do you have project that requires a multi-tenant user management with self-service for your customers? +Do you have a project that requires multi-tenant user management with self-service for your customers? Look no further — ZITADEL combines the ease of Auth0 with the versatility of Keycloak. -We provide you with a wide range of out of the box features to accelerate your project. +We provide you with a wide range of out-of-the-box features to accelerate your project. Multi-tenancy with branding customization, secure login, self-service, OpenID Connect, OAuth2.x, SAML2, LDAP, Passwordless with FIDO2 (including Passkeys), OTP, U2F, and an unlimited audit trail is there for you, ready to use. With ZITADEL you can rely on a hardened and extensible turnkey solution to solve all of your authentication and authorization needs. diff --git a/docs/docs/guides/integrate/login-ui/_select-account.mdx b/docs/docs/guides/integrate/login-ui/_select-account.mdx index 7bf31174c..ad83eb4e2 100644 --- a/docs/docs/guides/integrate/login-ui/_select-account.mdx +++ b/docs/docs/guides/integrate/login-ui/_select-account.mdx @@ -9,7 +9,7 @@ The list of session IDs can be sent in the “search sessions” request to get ```bash curl --request POST \ - --url https://$ZITADEL_DOMAIN/v2alpha/sessions/_search \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions/search \ --header 'Accept: application/json' \ --header 'Authorization: Bearer '"$TOKEN"''\ --header 'Content-Type: application/json' \ @@ -77,4 +77,4 @@ curl --request POST \ } ] } -``` \ No newline at end of file +``` diff --git a/docs/docs/guides/integrate/login-ui/external-login.mdx b/docs/docs/guides/integrate/login-ui/external-login.mdx index d92f5b3a1..74fe7c7f0 100644 --- a/docs/docs/guides/integrate/login-ui/external-login.mdx +++ b/docs/docs/guides/integrate/login-ui/external-login.mdx @@ -20,17 +20,18 @@ Send the following two URLs in the request body: 2. ErrorURL: Page that should be shown when an error happens during the authentication In the response, you will get an authentication URL of the provider you like. -[Start Identity Provider Flow Documentation](/docs/apis/resources/user_service/user-service-start-identity-provider-flow) +[Start Identity Provider Intent Documentation](/docs/apis/resources/user_service/user-service-start-identity-provider-intent) ### Request ```bash curl --request POST \ - --url https://$ZITADEL_DOMAIN/v2alpha/users/idps/$IDP_ID/start \ + --url https://$ZITADEL_DOMAIN/v2alpha/idp_intents/start \ --header 'Accept: application/json' \ --header 'Authorization: Bearer '"$TOKEN"''\ --header 'Content-Type: application/json' \ --data '{ + "idp_id": "$IDP_ID", "successUrl": "https://custom.com/login/idp/success", "failureUrl": "https://custom.com/login/idp/fail" }' @@ -62,15 +63,15 @@ After the user has successfully authenticated, a redirect to the ZITADEL backend ## Get Provider Information -ZITADEL will take the information of the provider. After this, a redirect will be made to either the success page in case of a successful login or to the error page in case of a failure will be performed. In the parameters, you will provide the intentID, a token, and optionally, if a user could be found, a user ID. +ZITADEL will take the information of the provider. After this, a redirect will be made to either the success page in case of a successful login or to the error page in case of a failure will be performed. In the parameters, you will provide the IDP intentID, a token, and optionally, if a user could be found, a user ID. To get the information of the provider, make a request to ZITADEL. -[Get Identity Provider Information Documentation](/docs/apis/resources/user_service/user-service-retrieve-identity-provider-information) +[Retrieve Identity Provider Intent Documentation](/docs/apis/resources/user_service/user-service-retrieve-identity-provider-intent) ### Request ```bash curl --request POST \ - --url https://$ZITADEL_DOMAIN/v2alpha/users/intents/$INTENT_ID/information \ + --url https://$ZITADEL_DOMAIN/v2alpha/idp_intents/$INTENT_ID \ --header 'Accept: application/json' \ --header 'Authorization: Bearer '"$TOKEN"''\ --header 'Content-Type: application/json' \ @@ -120,7 +121,7 @@ After successfully authenticating using your identity provider, you have three p ### Login If you did get a user ID in the parameters when calling your success page, you know that a user is already linked with the used identity provider and you are ready to perform the login. -Create a new session and include the intent ID and the token in the checks. +Create a new session and include the IDP intent ID and the token in the checks. This check requires that the previous step ended on the successful page and didn't’t result in an error. #### Request @@ -136,9 +137,9 @@ curl --request POST \ "user": { "userId": "218662596918640897" }, - "intent": { - "intentId": "219647325729980673", - "token": "k86ihn-VLMMUGKy1q1b5i_foECspKYqei1l4mS8LT7Xzjw" + "idpIntent": { + "idpIntentId": "219647325729980673", + "idpIntentToken": "k86ihn-VLMMUGKy1q1b5i_foECspKYqei1l4mS8LT7Xzjw" } } }' @@ -164,8 +165,8 @@ curl --request POST \ --data '{ "username": "minni-mouse@mouse.com", "profile": { - "firstName": "Minnie", - "lastName": "Mouse", + "givenName": "Minnie", + "familyName": "Mouse", "nickName": "Mini", "displayName": "Minnie Mouse", "preferredLanguage": "en", @@ -206,4 +207,4 @@ curl --request POST \ "displayName": "Minnie Mouse" } }' -``` \ No newline at end of file +``` diff --git a/docs/docs/guides/integrate/login-ui/mfa.mdx b/docs/docs/guides/integrate/login-ui/mfa.mdx index c2ae9d80e..05f2a5fe8 100644 --- a/docs/docs/guides/integrate/login-ui/mfa.mdx +++ b/docs/docs/guides/integrate/login-ui/mfa.mdx @@ -69,7 +69,7 @@ Request Example: ```bash curl --request POST \ - --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp/_verify \ + --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp/verify \ --header 'Accept: application/json' \ --header 'Authorization: Bearer '"$TOKEN"'' --header 'Content-Type: application/json' \ @@ -188,4 +188,4 @@ curl --request POST \ }' ``` -You have successfully registered a new U2F to the user. \ No newline at end of file +You have successfully registered a new U2F to the user. diff --git a/docs/docs/guides/integrate/login-ui/username-password.mdx b/docs/docs/guides/integrate/login-ui/username-password.mdx index 95f68d174..6729760ce 100644 --- a/docs/docs/guides/integrate/login-ui/username-password.mdx +++ b/docs/docs/guides/integrate/login-ui/username-password.mdx @@ -24,8 +24,8 @@ curl --request POST \ "userId": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a", "username": "minnie-mouse", "profile": { - "firstName": "Minnie", - "lastName": "Mouse", + "givenName": "Minnie", + "familyName": "Mouse", "nickName": "Mini", "displayName": "Minnie Mouse", "preferredLanguage": "en", diff --git a/internal/api/grpc/org/v2/org_integration_test.go b/internal/api/grpc/org/v2/org_integration_test.go index 570b0afa1..c8dbcff81 100644 --- a/internal/api/grpc/org/v2/org_integration_test.go +++ b/internal/api/grpc/org/v2/org_integration_test.go @@ -89,8 +89,8 @@ func TestServer_AddOrganization(t *testing.T) { UserType: &org.AddOrganizationRequest_Admin_Human{ Human: &user.AddHumanUserRequest{ Profile: &user.SetHumanProfile{ - FirstName: "firstname", - LastName: "lastname", + GivenName: "firstname", + FamilyName: "lastname", }, Email: &user.SetHumanEmail{ Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()), @@ -127,8 +127,8 @@ func TestServer_AddOrganization(t *testing.T) { UserType: &org.AddOrganizationRequest_Admin_Human{ Human: &user.AddHumanUserRequest{ Profile: &user.SetHumanProfile{ - FirstName: "firstname", - LastName: "lastname", + GivenName: "firstname", + FamilyName: "lastname", }, Email: &user.SetHumanEmail{ Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()), diff --git a/internal/api/grpc/org/v2/org_test.go b/internal/api/grpc/org/v2/org_test.go index c48421d66..2f2797ac7 100644 --- a/internal/api/grpc/org/v2/org_test.go +++ b/internal/api/grpc/org/v2/org_test.go @@ -74,8 +74,8 @@ func Test_addOrganizationRequestToCommand(t *testing.T) { UserType: &org.AddOrganizationRequest_Admin_Human{ Human: &user.AddHumanUserRequest{ Profile: &user.SetHumanProfile{ - FirstName: "firstname", - LastName: "lastname", + GivenName: "firstname", + FamilyName: "lastname", }, Email: &user.SetHumanEmail{ Email: "email@test.com", diff --git a/internal/api/grpc/session/v2/session.go b/internal/api/grpc/session/v2/session.go index 54e01ae64..b1a443a0d 100644 --- a/internal/api/grpc/session/v2/session.go +++ b/internal/api/grpc/session/v2/session.go @@ -251,8 +251,8 @@ func (s *Server) checksToCommand(ctx context.Context, checks *session.Checks) ([ if password := checks.GetPassword(); password != nil { sessionChecks = append(sessionChecks, command.CheckPassword(password.GetPassword())) } - if intent := checks.GetIntent(); intent != nil { - sessionChecks = append(sessionChecks, command.CheckIntent(intent.GetIntentId(), intent.GetToken())) + if intent := checks.GetIdpIntent(); intent != nil { + sessionChecks = append(sessionChecks, command.CheckIntent(intent.GetIdpIntentId(), intent.GetIdpIntentToken())) } if passkey := checks.GetWebAuthN(); passkey != nil { sessionChecks = append(sessionChecks, s.command.CheckWebAuthN(passkey.GetCredentialAssertionData())) diff --git a/internal/api/grpc/session/v2/session_integration_test.go b/internal/api/grpc/session/v2/session_integration_test.go index a8368f5c6..6c322bc28 100644 --- a/internal/api/grpc/session/v2/session_integration_test.go +++ b/internal/api/grpc/session/v2/session_integration_test.go @@ -263,9 +263,9 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), Checks: &session.Checks{ - Intent: &session.CheckIntent{ - IntentId: intentID, - Token: token, + IdpIntent: &session.CheckIDPIntent{ + IdpIntentId: intentID, + IdpIntentToken: token, }, }, }) @@ -294,9 +294,9 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), Checks: &session.Checks{ - Intent: &session.CheckIntent{ - IntentId: intentID, - Token: token, + IdpIntent: &session.CheckIDPIntent{ + IdpIntentId: intentID, + IdpIntentToken: token, }, }, }) @@ -306,9 +306,9 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), Checks: &session.Checks{ - Intent: &session.CheckIntent{ - IntentId: intentID, - Token: token, + IdpIntent: &session.CheckIDPIntent{ + IdpIntentId: intentID, + IdpIntentToken: token, }, }, }) @@ -336,9 +336,9 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), Checks: &session.Checks{ - Intent: &session.CheckIntent{ - IntentId: intentID, - Token: "false", + IdpIntent: &session.CheckIDPIntent{ + IdpIntentId: intentID, + IdpIntentToken: "false", }, }, }) diff --git a/internal/api/grpc/user/v2/user.go b/internal/api/grpc/user/v2/user.go index 78df98a90..cc24e05ad 100644 --- a/internal/api/grpc/user/v2/user.go +++ b/internal/api/grpc/user/v2/user.go @@ -72,8 +72,8 @@ func AddUserRequestToAddHuman(req *user.AddHumanUserRequest) (*command.AddHuman, return &command.AddHuman{ ID: req.GetUserId(), Username: username, - FirstName: req.GetProfile().GetFirstName(), - LastName: req.GetProfile().GetLastName(), + FirstName: req.GetProfile().GetGivenName(), + LastName: req.GetProfile().GetFamilyName(), NickName: req.GetProfile().GetNickName(), DisplayName: req.GetProfile().GetDisplayName(), Email: command.Email{ @@ -129,18 +129,18 @@ func (s *Server) AddIDPLink(ctx context.Context, req *user.AddIDPLinkRequest) (_ }, nil } -func (s *Server) StartIdentityProviderFlow(ctx context.Context, req *user.StartIdentityProviderFlowRequest) (_ *user.StartIdentityProviderFlowResponse, err error) { +func (s *Server) StartIdentityProviderIntent(ctx context.Context, req *user.StartIdentityProviderIntentRequest) (_ *user.StartIdentityProviderIntentResponse, err error) { switch t := req.GetContent().(type) { - case *user.StartIdentityProviderFlowRequest_Urls: + case *user.StartIdentityProviderIntentRequest_Urls: return s.startIDPIntent(ctx, req.GetIdpId(), t.Urls) - case *user.StartIdentityProviderFlowRequest_Ldap: + case *user.StartIdentityProviderIntentRequest_Ldap: return s.startLDAPIntent(ctx, req.GetIdpId(), t.Ldap) default: - return nil, errors.ThrowUnimplementedf(nil, "USERv2-S2g21", "type oneOf %T in method StartIdentityProviderFlow not implemented", t) + return nil, errors.ThrowUnimplementedf(nil, "USERv2-S2g21", "type oneOf %T in method StartIdentityProviderIntent not implemented", t) } } -func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.RedirectURLs) (*user.StartIdentityProviderFlowResponse, error) { +func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.RedirectURLs) (*user.StartIdentityProviderIntentResponse, error) { intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, urls.GetSuccessUrl(), urls.GetFailureUrl(), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err @@ -149,13 +149,13 @@ func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.Re if err != nil { return nil, err } - return &user.StartIdentityProviderFlowResponse{ + return &user.StartIdentityProviderIntentResponse{ Details: object.DomainToDetailsPb(details), - NextStep: &user.StartIdentityProviderFlowResponse_AuthUrl{AuthUrl: authURL}, + NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{AuthUrl: authURL}, }, nil } -func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderFlowResponse, error) { +func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderIntentResponse, error) { intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, "", "", authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err @@ -171,9 +171,9 @@ func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredenti if err != nil { return nil, err } - return &user.StartIdentityProviderFlowResponse{ + return &user.StartIdentityProviderIntentResponse{ Details: object.DomainToDetailsPb(details), - NextStep: &user.StartIdentityProviderFlowResponse_Intent{Intent: &user.Intent{IntentId: intentWriteModel.AggregateID, Token: token}}, + NextStep: &user.StartIdentityProviderIntentResponse_IdpIntent{IdpIntent: &user.IDPIntent{IdpIntentId: intentWriteModel.AggregateID, IdpIntentToken: token}}, }, nil } @@ -228,27 +228,27 @@ func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string return externalUser, userID, attributes, nil } -func (s *Server) RetrieveIdentityProviderInformation(ctx context.Context, req *user.RetrieveIdentityProviderInformationRequest) (_ *user.RetrieveIdentityProviderInformationResponse, err error) { - intent, err := s.command.GetIntentWriteModel(ctx, req.GetIntentId(), authz.GetCtxData(ctx).OrgID) +func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { + intent, err := s.command.GetIntentWriteModel(ctx, req.GetIdpIntentId(), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - if err := s.checkIntentToken(req.GetToken(), intent.AggregateID); err != nil { + if err := s.checkIntentToken(req.GetIdpIntentToken(), intent.AggregateID); err != nil { return nil, err } if intent.State != domain.IDPIntentStateSucceeded { return nil, errors.ThrowPreconditionFailed(nil, "IDP-Hk38e", "Errors.Intent.NotSucceeded") } - return intentToIDPInformationPb(intent, s.idpAlg) + return idpIntentToIDPIntentPb(intent, s.idpAlg) } -func intentToIDPInformationPb(intent *command.IDPIntentWriteModel, alg crypto.EncryptionAlgorithm) (_ *user.RetrieveIdentityProviderInformationResponse, err error) { +func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.EncryptionAlgorithm) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { rawInformation := new(structpb.Struct) err = rawInformation.UnmarshalJSON(intent.IDPUser) if err != nil { return nil, err } - information := &user.RetrieveIdentityProviderInformationResponse{ + information := &user.RetrieveIdentityProviderIntentResponse{ Details: intentToDetailsPb(intent), IdpInformation: &user.IDPInformation{ IdpId: intent.IDPID, diff --git a/internal/api/grpc/user/v2/user_integration_test.go b/internal/api/grpc/user/v2/user_integration_test.go index 18d97f98a..61ce038ff 100644 --- a/internal/api/grpc/user/v2/user_integration_test.go +++ b/internal/api/grpc/user/v2/user_integration_test.go @@ -67,8 +67,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -108,8 +108,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -153,8 +153,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -199,8 +199,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -246,8 +246,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -318,8 +318,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -352,8 +352,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -399,8 +399,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -451,8 +451,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -490,8 +490,8 @@ func TestServer_AddHumanUser(t *testing.T) { }, }, Profile: &user.SetHumanProfile{ - FirstName: "Donald", - LastName: "Duck", + GivenName: "Donald", + FamilyName: "Duck", NickName: gu.Ptr("Dukkie"), DisplayName: gu.Ptr("Donald Duck"), PreferredLanguage: gu.Ptr("en"), @@ -622,23 +622,23 @@ func TestServer_AddIDPLink(t *testing.T) { } } -func TestServer_StartIdentityProviderFlow(t *testing.T) { +func TestServer_StartIdentityProviderIntent(t *testing.T) { idpID := Tester.AddGenericOAuthProvider(t) type args struct { ctx context.Context - req *user.StartIdentityProviderFlowRequest + req *user.StartIdentityProviderIntentRequest } tests := []struct { name string args args - want *user.StartIdentityProviderFlowResponse + want *user.StartIdentityProviderIntentResponse wantErr bool }{ { name: "missing urls", args: args{ CTX, - &user.StartIdentityProviderFlowRequest{ + &user.StartIdentityProviderIntentRequest{ IdpId: idpID, }, }, @@ -649,9 +649,9 @@ func TestServer_StartIdentityProviderFlow(t *testing.T) { name: "next step auth url", args: args{ CTX, - &user.StartIdentityProviderFlowRequest{ + &user.StartIdentityProviderIntentRequest{ IdpId: idpID, - Content: &user.StartIdentityProviderFlowRequest_Urls{ + Content: &user.StartIdentityProviderIntentRequest_Urls{ Urls: &user.RedirectURLs{ SuccessUrl: "https://example.com/success", FailureUrl: "https://example.com/failure", @@ -659,12 +659,12 @@ func TestServer_StartIdentityProviderFlow(t *testing.T) { }, }, }, - want: &user.StartIdentityProviderFlowResponse{ + want: &user.StartIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.Now(), ResourceOwner: Tester.Organisation.ID, }, - NextStep: &user.StartIdentityProviderFlowResponse_AuthUrl{ + NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{ AuthUrl: "https://example.com/oauth/v2/authorize?client_id=clientID&prompt=select_account&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fidps%2Fcallback&response_type=code&scope=openid+profile+email&state=", }, }, @@ -673,7 +673,7 @@ func TestServer_StartIdentityProviderFlow(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := Client.StartIdentityProviderFlow(tt.args.ctx, tt.args.req) + got, err := Client.StartIdentityProviderIntent(tt.args.ctx, tt.args.req) if tt.wantErr { require.Error(t, err) } else { @@ -690,28 +690,28 @@ func TestServer_StartIdentityProviderFlow(t *testing.T) { } } -func TestServer_RetrieveIdentityProviderInformation(t *testing.T) { +func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { idpID := Tester.AddGenericOAuthProvider(t) intentID := Tester.CreateIntent(t, idpID) successfulID, token, changeDate, sequence := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", "id") ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Tester.CreateSuccessfulLDAPIntent(t, idpID, "", "id") type args struct { ctx context.Context - req *user.RetrieveIdentityProviderInformationRequest + req *user.RetrieveIdentityProviderIntentRequest } tests := []struct { name string args args - want *user.RetrieveIdentityProviderInformationResponse + want *user.RetrieveIdentityProviderIntentResponse wantErr bool }{ { name: "failed intent", args: args{ CTX, - &user.RetrieveIdentityProviderInformationRequest{ - IntentId: intentID, - Token: "", + &user.RetrieveIdentityProviderIntentRequest{ + IdpIntentId: intentID, + IdpIntentToken: "", }, }, wantErr: true, @@ -720,9 +720,9 @@ func TestServer_RetrieveIdentityProviderInformation(t *testing.T) { name: "wrong token", args: args{ CTX, - &user.RetrieveIdentityProviderInformationRequest{ - IntentId: successfulID, - Token: "wrong token", + &user.RetrieveIdentityProviderIntentRequest{ + IdpIntentId: successfulID, + IdpIntentToken: "wrong token", }, }, wantErr: true, @@ -731,12 +731,12 @@ func TestServer_RetrieveIdentityProviderInformation(t *testing.T) { name: "retrieve successful intent", args: args{ CTX, - &user.RetrieveIdentityProviderInformationRequest{ - IntentId: successfulID, - Token: token, + &user.RetrieveIdentityProviderIntentRequest{ + IdpIntentId: successfulID, + IdpIntentToken: token, }, }, - want: &user.RetrieveIdentityProviderInformationResponse{ + want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(changeDate), ResourceOwner: Tester.Organisation.ID, @@ -768,12 +768,12 @@ func TestServer_RetrieveIdentityProviderInformation(t *testing.T) { name: "retrieve successful ldap intent", args: args{ CTX, - &user.RetrieveIdentityProviderInformationRequest{ - IntentId: ldapSuccessfulID, - Token: ldapToken, + &user.RetrieveIdentityProviderIntentRequest{ + IdpIntentId: ldapSuccessfulID, + IdpIntentToken: ldapToken, }, }, - want: &user.RetrieveIdentityProviderInformationResponse{ + want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(ldapChangeDate), ResourceOwner: Tester.Organisation.ID, @@ -812,7 +812,7 @@ func TestServer_RetrieveIdentityProviderInformation(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := Client.RetrieveIdentityProviderInformation(tt.args.ctx, tt.args.req) + got, err := Client.RetrieveIdentityProviderIntent(tt.args.ctx, tt.args.req) if tt.wantErr { require.Error(t, err) } else { diff --git a/internal/api/grpc/user/v2/user_test.go b/internal/api/grpc/user/v2/user_test.go index a56e64107..768edad39 100644 --- a/internal/api/grpc/user/v2/user_test.go +++ b/internal/api/grpc/user/v2/user_test.go @@ -24,7 +24,7 @@ import ( var ignoreTypes = []protoreflect.FullName{"google.protobuf.Duration", "google.protobuf.Struct"} -func Test_intentToIDPInformationPb(t *testing.T) { +func Test_idpIntentToIDPIntentPb(t *testing.T) { decryption := func(err error) crypto.EncryptionAlgorithm { mCrypto := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)) mCrypto.EXPECT().Algorithm().Return("enc") @@ -44,7 +44,7 @@ func Test_intentToIDPInformationPb(t *testing.T) { alg crypto.EncryptionAlgorithm } type res struct { - resp *user.RetrieveIdentityProviderInformationResponse + resp *user.RetrieveIdentityProviderIntentResponse err error } tests := []struct { @@ -113,7 +113,7 @@ func Test_intentToIDPInformationPb(t *testing.T) { alg: decryption(nil), }, res{ - resp: &user.RetrieveIdentityProviderInformationResponse{ + resp: &user.RetrieveIdentityProviderIntentResponse{ Details: &object_pb.Details{ Sequence: 123, ChangeDate: timestamppb.New(time.Date(2019, 4, 1, 1, 1, 1, 1, time.Local)), @@ -166,7 +166,7 @@ func Test_intentToIDPInformationPb(t *testing.T) { }, }, res{ - resp: &user.RetrieveIdentityProviderInformationResponse{ + resp: &user.RetrieveIdentityProviderIntentResponse{ Details: &object_pb.Details{ Sequence: 123, ChangeDate: timestamppb.New(time.Date(2019, 4, 1, 1, 1, 1, 1, time.Local)), @@ -205,7 +205,7 @@ func Test_intentToIDPInformationPb(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := intentToIDPInformationPb(tt.args.intent, tt.args.alg) + got, err := idpIntentToIDPIntentPb(tt.args.intent, tt.args.alg) require.ErrorIs(t, err, tt.res.err) grpc.AllFieldsEqual(t, tt.res.resp.ProtoReflect(), got.ProtoReflect(), grpc.CustomMappers) }) diff --git a/internal/integration/client.go b/internal/integration/client.go index da7e0fb78..ab18c6452 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -87,8 +87,8 @@ func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse }, }, Profile: &user.SetHumanProfile{ - FirstName: "Mickey", - LastName: "Mouse", + GivenName: "Mickey", + FamilyName: "Mouse", }, Email: &user.SetHumanEmail{ Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()), diff --git a/proto/zitadel/session/v2alpha/session_service.proto b/proto/zitadel/session/v2alpha/session_service.proto index 693d6a710..4105413f4 100644 --- a/proto/zitadel/session/v2alpha/session_service.proto +++ b/proto/zitadel/session/v2alpha/session_service.proto @@ -79,7 +79,7 @@ service SessionService { // Search sessions rpc ListSessions (ListSessionsRequest) returns (ListSessionsResponse) { option (google.api.http) = { - post: "/v2alpha/sessions/_search" + post: "/v2alpha/sessions/search" body: "*" }; @@ -341,9 +341,9 @@ message Checks { description: "\"Checks the public key credential issued by the WebAuthN client. Requires that the user is already checked and a WebAuthN challenge to be requested, in any previous request.\""; } ]; - optional CheckIntent intent = 4 [ + optional CheckIDPIntent idp_intent = 4 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "\"Checks the intent. Requires that the userlink is already checked and a successful intent.\""; + description: "\"Checks the IDP intent. Requires that the userlink is already checked and a successful idp intent.\""; } ]; optional CheckTOTP totp = 5 [ @@ -397,20 +397,20 @@ message CheckWebAuthN { ]; } -message CheckIntent { - string intent_id = 1 [ +message CheckIDPIntent { + string idp_intent_id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "ID of the intent, previously returned on the success response of the IDP callback" + description: "ID of the idp intent, previously returned on the success response of the IDP callback" min_length: 1; max_length: 200; example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\""; } ]; - string token = 2 [ + string idp_intent_token = 2 [ (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "token of the intent, previously returned on the success response of the IDP callback" + description: "token of the idp intent, previously returned on the success response of the IDP callback" min_length: 1; max_length: 200; example: "\"SJKL3ioIDpo342ioqw98fjp3sdf32wahb=\""; diff --git a/proto/zitadel/user/v2alpha/idp.proto b/proto/zitadel/user/v2alpha/idp.proto index 07cce881c..2ced1ebb4 100644 --- a/proto/zitadel/user/v2alpha/idp.proto +++ b/proto/zitadel/user/v2alpha/idp.proto @@ -51,18 +51,18 @@ message RedirectURLs { ]; } -message Intent { - string intent_id = 1 [ +message IDPIntent { + string idp_intent_id = 1 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "ID of the intent" + description: "ID of the IDP intent" min_length: 1; max_length: 200; example: "\"163840776835432705=\""; } ]; - string token = 2 [ + string idp_intent_token = 2 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "token of the intent" + description: "token of the IDP intent" min_length: 1; max_length: 200; example: "\"SJKL3ioIDpo342ioqw98fjp3sdf32wahb=\""; diff --git a/proto/zitadel/user/v2alpha/user.proto b/proto/zitadel/user/v2alpha/user.proto index d6312eb3e..e82f3f968 100644 --- a/proto/zitadel/user/v2alpha/user.proto +++ b/proto/zitadel/user/v2alpha/user.proto @@ -20,7 +20,7 @@ enum Gender { } message SetHumanProfile { - string first_name = 1 [ + string given_name = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { @@ -29,7 +29,7 @@ message SetHumanProfile { example: "\"Minnie\""; } ]; - string last_name = 2 [ + string family_name = 2 [ (validate.rules).string = {min_len: 1, max_len: 200}, (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { diff --git a/proto/zitadel/user/v2alpha/user_service.proto b/proto/zitadel/user/v2alpha/user_service.proto index 19dea55de..908009134 100644 --- a/proto/zitadel/user/v2alpha/user_service.proto +++ b/proto/zitadel/user/v2alpha/user_service.proto @@ -137,7 +137,7 @@ service UserService { // Verify the email with the provided code rpc VerifyEmail (VerifyEmailRequest) returns (VerifyEmailResponse) { option (google.api.http) = { - post: "/v2alpha/users/{user_id}/email/_verify" + post: "/v2alpha/users/{user_id}/email/verify" body: "*" }; @@ -187,7 +187,7 @@ service UserService { // Verify the phone with the provided code rpc VerifyPhone (VerifyPhoneRequest) returns (VerifyPhoneResponse) { option (google.api.http) = { - post: "/v2alpha/users/{user_id}/phone/_verify" + post: "/v2alpha/users/{user_id}/phone/verify" body: "*" }; @@ -347,7 +347,7 @@ service UserService { rpc VerifyTOTPRegistration (VerifyTOTPRegistrationRequest) returns (VerifyTOTPRegistrationResponse) { option (google.api.http) = { - post: "/v2alpha/users/{user_id}/totp/_verify" + post: "/v2alpha/users/{user_id}/totp/verify" body: "*" }; @@ -459,9 +459,9 @@ service UserService { } // Start an IDP authentication (for external login, registration or linking) - rpc StartIdentityProviderFlow (StartIdentityProviderFlowRequest) returns (StartIdentityProviderFlowResponse) { + rpc StartIdentityProviderIntent (StartIdentityProviderIntentRequest) returns (StartIdentityProviderIntentResponse) { option (google.api.http) = { - post: "/v2alpha/users/idps/{idp_id}/start" + post: "/v2alpha/idp_intents/start" body: "*" }; @@ -483,9 +483,9 @@ service UserService { }; } - rpc RetrieveIdentityProviderInformation (RetrieveIdentityProviderInformationRequest) returns (RetrieveIdentityProviderInformationResponse) { + rpc RetrieveIdentityProviderIntent (RetrieveIdentityProviderIntentRequest) returns (RetrieveIdentityProviderIntentResponse) { option (google.api.http) = { - post: "/v2alpha/users/intents/{intent_id}/information" + post: "/v2alpha/idp_intents/{idp_intent_id}" body: "*" }; @@ -510,7 +510,7 @@ service UserService { // Link an IDP to an existing user rpc AddIDPLink (AddIDPLinkRequest) returns (AddIDPLinkResponse) { option (google.api.http) = { - post: "/v2alpha/users/users/{user_id}/links" + post: "/v2alpha/users/{user_id}/links" body: "*" }; @@ -1072,7 +1072,7 @@ message CreatePasskeyRegistrationLinkResponse{ ]; } -message StartIdentityProviderFlowRequest{ +message StartIdentityProviderIntentRequest{ string idp_id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { @@ -1089,7 +1089,7 @@ message StartIdentityProviderFlowRequest{ } } -message StartIdentityProviderFlowResponse{ +message StartIdentityProviderIntentResponse{ zitadel.object.v2alpha.Details details = 1; oneof next_step { string auth_url = 2 [ @@ -1098,28 +1098,28 @@ message StartIdentityProviderFlowResponse{ example: "\"https://accounts.google.com/o/oauth2/v2/auth?client_id=clientID&callback=https%3A%2F%2Fzitadel.cloud%2Fidps%2Fcallback\""; } ]; - Intent intent = 3 [ + IDPIntent idp_intent = 3 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "Intent information" + description: "IDP Intent information" } ]; } } -message RetrieveIdentityProviderInformationRequest{ - string intent_id = 1 [ +message RetrieveIdentityProviderIntentRequest{ + string idp_intent_id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "ID of the intent, previously returned on the success response of the IDP callback" + description: "ID of the idp intent, previously returned on the success response of the IDP callback" min_length: 1; max_length: 200; example: "\"163840776835432705\""; } ]; - string token = 2 [ + string idp_intent_token = 2 [ (validate.rules).string = {min_len: 1, max_len: 200}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "token of the intent, previously returned on the success response of the IDP callback" + description: "token of the idp intent, previously returned on the success response of the IDP callback" min_length: 1; max_length: 200; example: "\"SJKL3ioIDpo342ioqw98fjp3sdf32wahb=\""; @@ -1127,7 +1127,7 @@ message RetrieveIdentityProviderInformationRequest{ ]; } -message RetrieveIdentityProviderInformationResponse{ +message RetrieveIdentityProviderIntentResponse{ zitadel.object.v2alpha.Details details = 1; IDPInformation idp_information = 2; } From 637caba8a04b2cc2e880fc0c5099cc2e3d637e5a Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 22 Aug 2023 07:31:41 +0200 Subject: [PATCH 57/76] chore(workflow): exclude console dist folder from cache (#6414) * empty line for test * chore: exclude console dist folder from cache (cherry picked from commit f03a9d03b640c6ede89348922e136c210939288d) --- .github/workflows/console.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/console.yml b/.github/workflows/console.yml index 1c19982ac..bc4c5f089 100644 --- a/.github/workflows/console.yml +++ b/.github/workflows/console.yml @@ -32,7 +32,7 @@ jobs: timeout-minutes: 1 id: cache with: - key: console-${{ hashFiles('console', 'proto') }} + key: console-${{ hashFiles('console', 'proto', '!console/dist') }} restore-keys: | console- path: ${{ env.cache_path }} From d1ec248f052dce97bedeb15b817a6dce5a405fb0 Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Wed, 23 Aug 2023 14:57:20 +0200 Subject: [PATCH 58/76] feat(console): enable ID token mapping for generic OIDC provider (#6426) * fix: use IsIdTokenMapping request property * feat(console): oidc provider id token mapping * fix scss * reduce styles * fix lint --------- Co-authored-by: peintnermax Co-authored-by: Livio Spring (cherry picked from commit 29fa3d417c7d0fd3ae25e1fe7d8923d6ca0c388f) --- .../provider-oidc/provider-oidc.component.html | 8 ++++++++ .../provider-oidc/provider-oidc.component.scss | 8 ++++++++ .../provider-oidc/provider-oidc.component.ts | 8 ++++++++ console/src/assets/i18n/bg.json | 4 +++- console/src/assets/i18n/de.json | 4 +++- console/src/assets/i18n/en.json | 4 +++- console/src/assets/i18n/es.json | 4 +++- console/src/assets/i18n/fr.json | 4 +++- console/src/assets/i18n/it.json | 4 +++- console/src/assets/i18n/ja.json | 4 +++- console/src/assets/i18n/mk.json | 4 +++- console/src/assets/i18n/pl.json | 4 +++- console/src/assets/i18n/pt.json | 4 +++- console/src/assets/i18n/zh.json | 4 +++- internal/api/grpc/management/idp_converter.go | 13 +++++++------ proto/zitadel/idp.proto | 7 ++++++- 16 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 console/src/app/modules/providers/provider-oidc/provider-oidc.component.scss diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html index a33b4aa3d..41f835162 100644 --- a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html @@ -78,6 +78,14 @@
+
+ +
+

{{ 'IDP.ISIDTOKENMAPPING_DESC' | translate }}

+ {{ 'IDP.ISIDTOKENMAPPING' | translate }} +
+
+
{ @@ -131,6 +133,7 @@ export class ProviderOIDCComponent { req.setIssuer(this.issuer?.value); req.setScopesList(this.scopesList?.value); req.setProviderOptions(this.options); + req.setIsIdTokenMapping(this.isIdTokenMapping?.value); this.loading = true; this.service @@ -160,6 +163,7 @@ export class ProviderOIDCComponent { req.setIssuer(this.issuer?.value); req.setScopesList(this.scopesList?.value); req.setProviderOptions(this.options); + req.setIsIdTokenMapping(this.isIdTokenMapping?.value); this.loading = true; this.service @@ -224,4 +228,8 @@ export class ProviderOIDCComponent { public get scopesList(): AbstractControl | null { return this.form.get('scopesList'); } + + public get isIdTokenMapping(): AbstractControl | null { + return this.form.get('isIdTokenMapping'); + } } diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json index 4c10757a6..1bceff83f 100644 --- a/console/src/assets/i18n/bg.json +++ b/console/src/assets/i18n/bg.json @@ -1822,7 +1822,9 @@ "DELETED": "Idp премахнат успешно!", "ADDED": "Добавено успешно.", "REMOVED": "Премахнато успешно." - } + }, + "ISIDTOKENMAPPING": "Съответствие от ID токен", + "ISIDTOKENMAPPING_DESC": "Ако е избрано, информацията на доставчика се съответства от ID токена, а не от userinfo крайната точка." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index d97876fbe..9a0c18994 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1831,7 +1831,9 @@ "DELETED": "Idp erfolgreich gelöscht!", "ADDED": "Erfolgreich hinzugefügt.", "REMOVED": "Erfolgreich entfernt." - } + }, + "ISIDTOKENMAPPING": "Zuordnung vom ID-Token", + "ISIDTOKENMAPPING_DESC": "Legt fest, ob für das Mapping der Provider Informationen das ID-Token verwendet werden soll, anstatt des Userinfo-Endpoints." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 34c0fce96..c1a8f2b6d 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1828,7 +1828,9 @@ "DELETED": "Idp removed successfully!", "ADDED": "Added successfully.", "REMOVED": "Removed successfully." - } + }, + "ISIDTOKENMAPPING": "Map from the ID token", + "ISIDTOKENMAPPING_DESC": "If selected, provider information gets mapped from the ID token, not from the userinfo endpoint." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index acd565e59..afe86e5aa 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -1828,7 +1828,9 @@ "DELETED": "¡IDP eliminado con éxito!", "ADDED": "Añadido con éxito.", "REMOVED": "Eliminado con éxito." - } + }, + "ISIDTOKENMAPPING": "Asignación del ID token", + "ISIDTOKENMAPPING_DESC": "Si se selecciona, la información del proveedor se asigna desde el ID token, no desde el punto final de userinfo." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index c4b7dd3bf..bd4d6bf0f 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1832,7 +1832,9 @@ "DELETED": "Idp supprimé avec succès !", "ADDED": "Ajouté avec succès.", "REMOVED": "Suppression réussie." - } + }, + "ISIDTOKENMAPPING": "Mappage depuis le jeton ID", + "ISIDTOKENMAPPING_DESC": "Si sélectionné, les informations du fournisseur sont mappées à partir du jeton ID, et non à partir du point d'extrémité userinfo." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index e14a12365..57d0383c3 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1832,7 +1832,9 @@ "DELETED": "IDP rimosso con successo!", "ADDED": "Aggiunto con successo.", "REMOVED": "Rimosso con successo." - } + }, + "ISIDTOKENMAPPING": "Mappatura dal token ID", + "ISIDTOKENMAPPING_DESC": "Se selezionato, le informazioni del provider vengono mappate dal token ID, non dal punto finale userinfo." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 6eb5718f6..c5fd92746 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1823,7 +1823,9 @@ "DELETED": "IDPは正常に削除されました!", "ADDED": "正常に追加されました。", "REMOVED": "正常に削除されました。" - } + }, + "ISIDTOKENMAPPING": "IDトークンからのマッピング", + "ISIDTOKENMAPPING_DESC": "選択された場合、プロバイダ情報はIDトークンからマッピングされ、userinfoエンドポイントからではありません。" }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json index 81b600188..f01d931d8 100644 --- a/console/src/assets/i18n/mk.json +++ b/console/src/assets/i18n/mk.json @@ -1828,7 +1828,9 @@ "DELETED": "IDP успешно отстранет!", "ADDED": "Успешно додадено.", "REMOVED": "Успешно отстрането." - } + }, + "ISIDTOKENMAPPING": "Совпаѓање од ID токен", + "ISIDTOKENMAPPING_DESC": "Ако е избрано, информациите од провајдерот се мапираат од ID токенот, а не од userinfo крајната точка." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 0c74e3de2..71aef9be5 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1832,7 +1832,9 @@ "DELETED": "Dostawca tożsamości usunięty pomyślnie!", "ADDED": "Dodano pomyślnie.", "REMOVED": "Usunięto pomyślnie." - } + }, + "ISIDTOKENMAPPING": "Mapowanie z tokena ID", + "ISIDTOKENMAPPING_DESC": "Jeśli wybrane, informacje dostawcy są mapowane z tokena ID, a nie z punktu końcowego userinfo." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 6f8d76c68..7ff90c008 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -1826,7 +1826,9 @@ "DELETED": "Provedor de identidade removido com sucesso!", "ADDED": "Adicionado com sucesso.", "REMOVED": "Removido com sucesso." - } + }, + "ISIDTOKENMAPPING": "Mapeamento do token ID", + "ISIDTOKENMAPPING_DESC": "Se selecionado, as informações do provedor são mapeadas a partir do token ID, e não do ponto final userinfo." }, "MFA": { "LIST": { diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 3d7aaefed..983580201 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1831,7 +1831,9 @@ "DELETED": "IDP 删除成功!", "ADDED": "添加成功。", "REMOVED": "成功删除。" - } + }, + "ISIDTOKENMAPPING": "从ID令牌映射", + "ISIDTOKENMAPPING_DESC": "如果选中,提供商信息将从ID令牌映射,而不是从userinfo端点。" }, "MFA": { "LIST": { diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index 6b0becd98..efce720a8 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -248,12 +248,13 @@ func updateGenericOAuthProviderToCommand(req *mgmt_pb.UpdateGenericOAuthProvider func addGenericOIDCProviderToCommand(req *mgmt_pb.AddGenericOIDCProviderRequest) command.GenericOIDCProvider { return command.GenericOIDCProvider{ - Name: req.Name, - Issuer: req.Issuer, - ClientID: req.ClientId, - ClientSecret: req.ClientSecret, - Scopes: req.Scopes, - IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IsIDTokenMapping: req.IsIdTokenMapping, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } } diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto index 645642f30..b8c388bb6 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -342,7 +342,12 @@ message GenericOIDCConfig { description: "the scopes requested by ZITADEL during the request on the identity provider"; } ]; - bool is_id_token_mapping = 4; + bool is_id_token_mapping = 4 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "true"; + description: "if true, provider information get mapped from the id token, not from the userinfo endpoint"; + } + ]; } message GitHubConfig { From 379437f1d62f09eaf5a928dcb66a8f7238536d71 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Thu, 24 Aug 2023 12:31:12 +0200 Subject: [PATCH 59/76] fix(api): handle id_token_mapping in generic oidc provider correctly (#6428) (cherry picked from commit 94d13fd3e13307cc78bd760f1c446650eb73d3fb) --- internal/api/grpc/admin/idp_converter.go | 26 ++++++++++--------- internal/api/grpc/management/idp_converter.go | 13 +++++----- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index 7aeb7ec33..c9dd3ae08 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -231,23 +231,25 @@ func updateGenericOAuthProviderToCommand(req *admin_pb.UpdateGenericOAuthProvide func addGenericOIDCProviderToCommand(req *admin_pb.AddGenericOIDCProviderRequest) command.GenericOIDCProvider { return command.GenericOIDCProvider{ - Name: req.Name, - Issuer: req.Issuer, - ClientID: req.ClientId, - ClientSecret: req.ClientSecret, - Scopes: req.Scopes, - IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IsIDTokenMapping: req.IsIdTokenMapping, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } } func updateGenericOIDCProviderToCommand(req *admin_pb.UpdateGenericOIDCProviderRequest) command.GenericOIDCProvider { return command.GenericOIDCProvider{ - Name: req.Name, - Issuer: req.Issuer, - ClientID: req.ClientId, - ClientSecret: req.ClientSecret, - Scopes: req.Scopes, - IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IsIDTokenMapping: req.IsIdTokenMapping, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } } diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index efce720a8..0d70aca0d 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -260,12 +260,13 @@ func addGenericOIDCProviderToCommand(req *mgmt_pb.AddGenericOIDCProviderRequest) func updateGenericOIDCProviderToCommand(req *mgmt_pb.UpdateGenericOIDCProviderRequest) command.GenericOIDCProvider { return command.GenericOIDCProvider{ - Name: req.Name, - Issuer: req.Issuer, - ClientID: req.ClientId, - ClientSecret: req.ClientSecret, - Scopes: req.Scopes, - IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IsIDTokenMapping: req.IsIdTokenMapping, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), } } From 4f7254f121a505609d7be8689b838c37ef4148e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Fri, 1 Sep 2023 13:32:13 +0300 Subject: [PATCH 60/76] fix(deps): upgrade oidc and otel (#6468) --- go.mod | 153 +++---- go.sum | 432 ++++++++---------- internal/telemetry/metrics/metrics.go | 9 +- .../telemetry/metrics/otel/open_telemetry.go | 17 +- 4 files changed, 281 insertions(+), 330 deletions(-) diff --git a/go.mod b/go.mod index 07620215d..51ac03cb3 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,26 @@ module github.com/zitadel/zitadel go 1.20 require ( - cloud.google.com/go/storage v1.30.1 - github.com/BurntSushi/toml v1.2.1 + cloud.google.com/go/storage v1.32.0 + github.com/BurntSushi/toml v1.3.2 github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.19.1 github.com/Masterminds/squirrel v1.5.4 github.com/VictoriaMetrics/fastcache v1.12.1 github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b github.com/allegro/bigcache v1.2.1 - github.com/benbjohnson/clock v1.3.0 + github.com/benbjohnson/clock v1.3.5 github.com/boombuler/barcode v1.0.1 - github.com/cockroachdb/cockroach-go/v2 v2.3.3 + github.com/cockroachdb/cockroach-go/v2 v2.3.5 github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be github.com/descope/virtualwebauthn v1.0.2 - github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 - github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 + github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e + github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 github.com/drone/envsubst v1.0.3 - github.com/envoyproxy/protoc-gen-validate v0.10.1 - github.com/fatih/color v1.13.0 - github.com/go-ldap/ldap/v3 v3.4.4 - github.com/go-webauthn/webauthn v0.8.2 + github.com/envoyproxy/protoc-gen-validate v1.0.2 + github.com/fatih/color v1.15.0 + github.com/go-ldap/ldap/v3 v3.4.5 + github.com/go-webauthn/webauthn v0.8.6 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 github.com/gorilla/csrf v1.7.1 @@ -31,19 +31,19 @@ require ( github.com/gorilla/securecookie v1.1.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 github.com/h2non/gock v1.2.0 github.com/improbable-eng/grpc-web v0.15.0 - github.com/jackc/pgconn v1.14.0 + github.com/jackc/pgconn v1.14.1 github.com/jackc/pgtype v1.14.0 github.com/jackc/pgx/v4 v4.18.1 github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 github.com/jinzhu/gorm v1.9.16 - github.com/k3a/html2text v1.1.0 + github.com/k3a/html2text v1.2.1 github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2 - github.com/lib/pq v1.10.7 + github.com/lib/pq v1.10.9 github.com/lucasb-eyer/go-colorful v1.2.0 - github.com/minio/minio-go/v7 v7.0.50 + github.com/minio/minio-go/v7 v7.0.63 github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/gamut v0.3.1 github.com/muhlemmer/gu v0.3.1 @@ -52,78 +52,78 @@ require ( github.com/pquerna/otp v1.4.0 github.com/rakyll/statik v0.1.7 github.com/rs/cors v1.9.0 - github.com/sony/sonyflake v1.1.0 + github.com/sony/sonyflake v1.2.0 github.com/spf13/cobra v1.7.0 - github.com/spf13/viper v1.15.0 + github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.4 github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 - github.com/zitadel/logging v0.3.4 - github.com/zitadel/oidc/v2 v2.7.0 + github.com/zitadel/logging v0.4.0 + github.com/zitadel/oidc/v2 v2.10.0 github.com/zitadel/passwap v0.3.0 github.com/zitadel/saml v0.1.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 - go.opentelemetry.io/otel v1.14.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 - go.opentelemetry.io/otel/exporters/prometheus v0.37.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 - go.opentelemetry.io/otel/metric v0.37.0 - go.opentelemetry.io/otel/sdk v1.14.0 - go.opentelemetry.io/otel/sdk/metric v0.37.0 - go.opentelemetry.io/otel/trace v1.14.0 - golang.org/x/crypto v0.11.0 - golang.org/x/net v0.12.0 - golang.org/x/oauth2 v0.10.0 - golang.org/x/sync v0.2.0 - golang.org/x/text v0.11.0 - google.golang.org/api v0.126.0 - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc - google.golang.org/grpc v1.55.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 + go.opentelemetry.io/otel v1.17.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 + go.opentelemetry.io/otel/exporters/prometheus v0.40.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 + go.opentelemetry.io/otel/metric v1.17.0 + go.opentelemetry.io/otel/sdk v1.17.0 + go.opentelemetry.io/otel/sdk/metric v0.40.0 + go.opentelemetry.io/otel/trace v1.17.0 + golang.org/x/crypto v0.12.0 + golang.org/x/net v0.14.0 + golang.org/x/oauth2 v0.11.0 + golang.org/x/sync v0.3.0 + golang.org/x/text v0.12.0 + google.golang.org/api v0.138.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d + google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 gopkg.in/square/go-jose.v2 v2.6.0 sigs.k8s.io/yaml v1.3.0 ) require ( - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.43.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect - github.com/go-webauthn/revoke v0.1.9 // indirect + github.com/go-webauthn/x v0.1.4 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/glog v1.1.1 // indirect - github.com/google/go-tpm v0.3.3 // indirect - github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect - github.com/google/s2a-go v0.1.4 // indirect + github.com/google/go-tpm v0.9.0 // indirect + github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect + github.com/google/s2a-go v0.1.5 // indirect github.com/gorilla/websocket v1.4.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/smartystreets/assertions v1.0.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect ) require ( - cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute v1.20.1 // indirect + cloud.google.com/go v0.110.7 // indirect + cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.0.0 // indirect - cloud.google.com/go/trace v1.9.0 // indirect + cloud.google.com/go/iam v1.1.2 // indirect + cloud.google.com/go/trace v1.10.1 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/amdonov/xmlsig v0.1.0 // indirect github.com/beevik/etree v1.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect - github.com/dlclark/regexp2 v1.8.1 // indirect - github.com/dsoprea/go-exif v0.0.0-20221012082141-d21ac8e2de85 // indirect - github.com/dsoprea/go-exif/v2 v2.0.0-20221012082141-d21ac8e2de85 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d // indirect + github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect github.com/dsoprea/go-jpeg-image-structure v0.0.0-20221012074422-4f3f7e934102 // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect @@ -133,21 +133,20 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc // indirect + github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect @@ -163,38 +162,38 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 // indirect github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 // indirect - github.com/klauspost/compress v1.16.4 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect github.com/muesli/kmeans v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/rs/xid v1.4.0 // indirect + github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/russellhaering/goxmldsig v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - golang.org/x/sys v0.10.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + golang.org/x/sys v0.11.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index c061a1206..f658e0227 100644 --- a/go.sum +++ b/go.sum @@ -17,25 +17,28 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc= -cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew= +cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= +cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/monitoring v1.15.1 h1:65JhLMd+JiYnXr6j5Z63dUYCuOg770p8a/VC+gil/58= +cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -46,32 +49,31 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/storage v1.32.0 h1:5w6DxEGOnktmJHarxAOUywxVW9lbNWIzlzzUltG/3+o= +cloud.google.com/go/storage v1.32.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/OxflYkiD8= +cloud.google.com/go/trace v1.10.1 h1:EwGdOLCNfYOOPtgqo+D2sDLZmRCEO1AagRTJCU6ztdg= +cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 h1:aRQEQ57Mw12h0tG8oo2UGC5d8fpUFCvD1lcS9fdGh6I= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0/go.mod h1:WzE/bKzbWw91rEv1w53y4taJheFSkUzp2Mu8uItorHg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.37.0 h1:+3ioRuJPNQk6WkbdClWVo8FyfpXu/ZXePQ3syDwrN4o= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 h1:k5x4SiDgKS8wVQO/ww1fnoh5gwYEg6Wsi+1z5kB9uDM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0/go.mod h1:oEccMakRmMNrayCPR+5OmZE/aeXmTPzUtmomEXIPBdI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.19.1 h1:lP8YpTi26Bei2OrXpQEUnNFPqKT6bTn3P8DvJC4i8WQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.19.1/go.mod h1:g9zEQ45EhrGGA6HyCtxi8yL0BZ0vD+pVaqSkiLjVIzY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.43.1 h1:EA/FmSYRyeL2ZogHD8ZCPAt96UZh/U76wQjGhzRFEHE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.43.1/go.mod h1:OZ0OdcedAJJyQbJsfO97KMimDYkuOkzzO4AQPgV5QRI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.43.1 h1:ti4stlXHjDhGl+1h+EpqXv9+Wxv0XqCB3XTT4W6ZoQU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.43.1/go.mod h1:lv7cjEH/BKG+7xh3vR4T8//UkWZ9eIkgAk6HpN/T6rk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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= @@ -88,6 +90,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -98,7 +102,6 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= @@ -109,8 +112,8 @@ github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62 github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -122,10 +125,9 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -141,30 +143,25 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4= -github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= +github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= +github.com/cockroachdb/cockroach-go/v2 v2.3.5/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -178,28 +175,27 @@ github.com/descope/virtualwebauthn v1.0.2/go.mod h1:iJvinjD1iZYqQ09J5lF0+795OdDb github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= -github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= -github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 h1:xkbJGxVnk5sM8/LXeTKaBOfAZrI+iqvIPyH8oK1c6CQ= -github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= +github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog= +github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 h1:QDuDMgEkC/lnmvk0d/fZfcUUml18uUbS9TY5QtbdFhs= -github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0= +github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 h1:ObbB2tzHWWAxzsG5futqeq2Ual2zYlo/+eMkSc5sn8w= +github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562/go.mod h1:X2TOTJ+Uamd454RFp7ig2tmP3hQg0Z2Qk8gbVQmU0mk= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= -github.com/dsoprea/go-exif v0.0.0-20221012082141-d21ac8e2de85 h1:+wKSjK8wh5Ybb63Fx1QvTSIfWU/n1nmqEzZy+82pqbQ= -github.com/dsoprea/go-exif v0.0.0-20221012082141-d21ac8e2de85/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= +github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d h1:ygcRCGNKuEiA98k7X35hknEN8RIRUF1jrz7k1rZCvsk= +github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0= -github.com/dsoprea/go-exif/v2 v2.0.0-20221012082141-d21ac8e2de85 h1:sgMOHQHMiddteUitH9QYgaRTwAcqqr5X9z5j4yNXDuY= -github.com/dsoprea/go-exif/v2 v2.0.0-20221012082141-d21ac8e2de85/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc= +github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d h1:yeH8wrJa3+8uKKDAdURHUK1ds2UvKhMqX2MiOdVeKPs= +github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc= github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= @@ -236,27 +232,27 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -276,12 +272,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= -github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= +github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= +github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -301,19 +296,22 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= -github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= -github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= -github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= +github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= +github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= +github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -324,21 +322,19 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc h1:WkAZHSmcnJhZyutVoVXe7lDSQBbISxITcm57tYf22PE= -github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= +github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= +github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -387,21 +383,17 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= -github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= -github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= -github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -413,21 +405,21 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk= -github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= +github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg= +github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -445,23 +437,19 @@ github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlI github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= @@ -512,8 +500,9 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= +github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -553,6 +542,7 @@ github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52/go.mod h1:RDZ+4PR3mDOtTpVbI0qBE+rdhmtIrtbssiNn38/1OWA= github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= +github.com/jeremija/gosubmit v0.2.7/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= @@ -560,6 +550,7 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -579,8 +570,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/k3a/html2text v1.1.0 h1:ks4hKSTdiTRsLr0DM771mI5TvsoG6zH7m1Ulv7eJRHw= -github.com/k3a/html2text v1.1.0/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA= +github.com/k3a/html2text v1.2.1 h1:nvnKgBvBR/myqrwfLuiqecUtaK1lB9hGziIJKatNFVY= +github.com/k3a/html2text v1.2.1/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA= github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 h1:K8qael4LemsmJCGt+ccI8b0fCNFDttmEu3qtpFt3G0M= github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw= github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 h1:2b9anKtyO/UTUQb+TAdPAW+w0p9xCIvng4fdZJ2xsYk= @@ -592,12 +583,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= -github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -605,8 +595,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -623,30 +614,28 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= @@ -657,13 +646,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= -github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ= +github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -704,7 +692,6 @@ github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYo github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -721,9 +708,8 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -741,39 +727,34 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -782,18 +763,18 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -809,7 +790,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -819,31 +799,22 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sony/sonyflake v1.1.0 h1:wnrEcL3aOkWmPlhScLEGAXKkLAIslnBteNUq4Bw6MM4= -github.com/sony/sonyflake v1.1.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ= +github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -859,25 +830,21 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c= github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203/go.mod h1:0Xw5cYMOYpgaWs+OOSx41ugycl2qvKTi9tlMMcZhFyY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w= github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4= github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -886,7 +853,6 @@ github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -896,17 +862,14 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -github.com/zitadel/logging v0.3.4 h1:9hZsTjMMTE3X2LUi0xcF9Q9EdLo+FAezeu52ireBbHM= -github.com/zitadel/logging v0.3.4/go.mod h1:aPpLQhE+v6ocNK0TWrBrd363hZ95KcI17Q1ixAQwZF0= -github.com/zitadel/oidc/v2 v2.7.0 h1:IGX4EDk6tegTjUSsZDWeTfLseFU0BdJ/Glf1tgys2lU= -github.com/zitadel/oidc/v2 v2.7.0/go.mod h1:zkUkVJS0sDVy9m0UA9RgO3f8i/C0rtjvXU36UJj7T+0= +github.com/zitadel/logging v0.4.0 h1:lRAIFgaRoJpLNbsL7jtIYHcMDoEJP9QZB4GqMfl4xaA= +github.com/zitadel/logging v0.4.0/go.mod h1:6uALRJawpkkuUPCkgzfgcPR3c2N908wqnOnIrRelUFc= +github.com/zitadel/oidc/v2 v2.10.0 h1:mKCOA1SF7R+XmKmicbOAMY2wxp3szZMuM3IzrkBGplQ= +github.com/zitadel/oidc/v2 v2.10.0/go.mod h1:rEM7F10FKuieuQUlQf9fRoSiTkSs8rx5Dj4SgfsMPWU= github.com/zitadel/passwap v0.3.0 h1:kC/vzN9xQlEQjUAZs0z2P5nKrZs9AuTqprteSQ2S4Ag= github.com/zitadel/passwap v0.3.0/go.mod h1:sIpG6HfmnP28qwxu8kf+ot53ERbLwU9fOITstAwZSms= -github.com/zitadel/saml v0.0.12-0.20230809152136-3bdfd7ad13fe h1:mcdiFREwddgcJjK+KDl/J29Cikr0nymiFhYdp3DBKB4= -github.com/zitadel/saml v0.0.12-0.20230809152136-3bdfd7ad13fe/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM= github.com/zitadel/saml v0.1.0 h1:FZKKFRCamoKmFH3kGOW0ObcDozaJz7NdHn+WPm8PcXc= github.com/zitadel/saml v0.1.0/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -919,33 +882,31 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0/go.mod h1:UMklln0+MRhZC4e3PwmN3pCtq4DyIadWw4yikh6bNrw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM= -go.opentelemetry.io/otel/exporters/prometheus v0.37.0 h1:NQc0epfL0xItsmGgSXgfbH2C1fq2VLXkZoDFsfRNHpc= -go.opentelemetry.io/otel/exporters/prometheus v0.37.0/go.mod h1:hB8qWjsStK36t50/R0V2ULFb4u95X/Q6zupXLgvjTh8= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 h1:sEL90JjOO/4yhquXl5zTAkLLsZ5+MycAgX99SDsxGc8= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0/go.mod h1:oCslUcizYdpKYyS9e8srZEqM6BB8fq41VJBjLAE6z1w= -go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs= -go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s= -go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= -go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= -go.opentelemetry.io/otel/sdk/metric v0.37.0 h1:haYBBtZZxiI3ROwSmkZnI+d0+AVzBWeviuYQDeBWosU= -go.opentelemetry.io/otel/sdk/metric v0.37.0/go.mod h1:mO2WV1AZKKwhwHTV3AKOoIEb9LbUaENZDuGUQd+j4A0= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 h1:HKORGpiOY0R0nAPtKx/ub8/7XoHhRooP8yNRkuPfelI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0/go.mod h1:e+y1M74SYXo/FcIx3UATwth2+5dDkM8dBi7eXg1tbw8= +go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= +go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 h1:U5GYackKpVKlPrd/5gKMlrTlP2dCESAAFU682VCpieY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0/go.mod h1:aFsJfCEnLzEu9vRRAcUiB/cpRTbVsNdF3OHSPpdjxZQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 h1:iGeIsSYwpYSvh5UGzWrJfTDJvPjrXtxl3GUppj6IXQU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0/go.mod h1:1j3H3G1SBYpZFti6OI4P0uRQCW20MXkG5v4UWXppLLE= +go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ERB6R034zmsHjBRLyxrAR8= +go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= +go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= +go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= +go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= +go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= +go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= +go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= +go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= +go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -953,12 +914,11 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -966,7 +926,6 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -983,11 +942,11 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -999,6 +958,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1013,7 +974,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1026,6 +986,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1041,7 +1002,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1074,10 +1034,11 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1087,9 +1048,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1102,8 +1062,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1112,7 +1073,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1161,43 +1121,40 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1262,6 +1219,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1290,8 +1248,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.138.0 h1:K/tVp05MxNVbHShRw9m7e9VJGdagNeTdMzqPH7AUqr0= +google.golang.org/api v0.138.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1341,13 +1299,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1372,11 +1329,9 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1389,7 +1344,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/internal/telemetry/metrics/metrics.go b/internal/telemetry/metrics/metrics.go index 1ebbae0c0..503ebc22d 100644 --- a/internal/telemetry/metrics/metrics.go +++ b/internal/telemetry/metrics/metrics.go @@ -6,7 +6,6 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument" ) const ( @@ -23,8 +22,8 @@ type Metrics interface { GetMetricsProvider() metric.MeterProvider RegisterCounter(name, description string) error AddCount(ctx context.Context, name string, value int64, labels map[string]attribute.Value) error - RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error - RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error + RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64Callback) error + RegisterValueObserver(name, description string, callbackFunc metric.Int64Callback) error } var M Metrics @@ -57,14 +56,14 @@ func AddCount(ctx context.Context, name string, value int64, labels map[string]a return M.AddCount(ctx, name, value, labels) } -func RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error { +func RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64Callback) error { if M == nil { return nil } return M.RegisterUpDownSumObserver(name, description, callbackFunc) } -func RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error { +func RegisterValueObserver(name, description string, callbackFunc metric.Int64Callback) error { if M == nil { return nil } diff --git a/internal/telemetry/metrics/otel/open_telemetry.go b/internal/telemetry/metrics/otel/open_telemetry.go index e249f00d8..0afafad23 100644 --- a/internal/telemetry/metrics/otel/open_telemetry.go +++ b/internal/telemetry/metrics/otel/open_telemetry.go @@ -9,7 +9,6 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument" sdk_metric "go.opentelemetry.io/otel/sdk/metric" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -56,7 +55,7 @@ func (m *Metrics) RegisterCounter(name, description string) error { if _, exists := m.Counters.Load(name); exists { return nil } - counter, err := m.Meter.Int64Counter(name, instrument.WithDescription(description)) + counter, err := m.Meter.Int64Counter(name, metric.WithDescription(description)) if err != nil { return err } @@ -69,16 +68,16 @@ func (m *Metrics) AddCount(ctx context.Context, name string, value int64, labels if !exists { return caos_errs.ThrowNotFound(nil, "METER-4u8fs", "Errors.Metrics.Counter.NotFound") } - counter.(instrument.Int64Counter).Add(ctx, value, MapToKeyValue(labels)...) + counter.(metric.Int64Counter).Add(ctx, value, MapToAddOption(labels)...) return nil } -func (m *Metrics) RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error { +func (m *Metrics) RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64Callback) error { if _, exists := m.UpDownSumObserver.Load(name); exists { return nil } - counter, err := m.Meter.Int64ObservableUpDownCounter(name, instrument.WithInt64Callback(callbackFunc), instrument.WithDescription(description)) + counter, err := m.Meter.Int64ObservableUpDownCounter(name, metric.WithInt64Callback(callbackFunc), metric.WithDescription(description)) if err != nil { return err } @@ -87,12 +86,12 @@ func (m *Metrics) RegisterUpDownSumObserver(name, description string, callbackFu return nil } -func (m *Metrics) RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error { +func (m *Metrics) RegisterValueObserver(name, description string, callbackFunc metric.Int64Callback) error { if _, exists := m.UpDownSumObserver.Load(name); exists { return nil } - gauge, err := m.Meter.Int64ObservableGauge(name, instrument.WithInt64Callback(callbackFunc), instrument.WithDescription(description)) + gauge, err := m.Meter.Int64ObservableGauge(name, metric.WithInt64Callback(callbackFunc), metric.WithDescription(description)) if err != nil { return err } @@ -101,7 +100,7 @@ func (m *Metrics) RegisterValueObserver(name, description string, callbackFunc i return nil } -func MapToKeyValue(labels map[string]attribute.Value) []attribute.KeyValue { +func MapToAddOption(labels map[string]attribute.Value) []metric.AddOption { if labels == nil { return nil } @@ -112,5 +111,5 @@ func MapToKeyValue(labels map[string]attribute.Value) []attribute.KeyValue { Value: value, }) } - return keyValues + return []metric.AddOption{metric.WithAttributes(keyValues...)} } From 3b0be676278d68ae090320b4c9953ea6e8c4d945 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 5 Sep 2023 07:18:47 +0200 Subject: [PATCH 61/76] cleanup merge --- internal/command/crypto.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/internal/command/crypto.go b/internal/command/crypto.go index 0d94526b8..0f7fe11ce 100644 --- a/internal/command/crypto.go +++ b/internal/command/crypto.go @@ -91,26 +91,3 @@ func secretGeneratorConfigWithDefault(ctx context.Context, filter preparation.Fi IncludeSymbols: wm.IncludeSymbols, }, nil } - -func secretGeneratorConfigWithDefault(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, defaultGenerator *crypto.GeneratorConfig) (*crypto.GeneratorConfig, error) { - wm := NewInstanceSecretGeneratorConfigWriteModel(ctx, typ) - events, err := filter(ctx, wm.Query()) - if err != nil { - return nil, err - } - wm.AppendEvents(events...) - if err := wm.Reduce(); err != nil { - return nil, err - } - if wm.State != domain.SecretGeneratorStateActive { - return defaultGenerator, nil - } - return &crypto.GeneratorConfig{ - Length: wm.Length, - Expiry: wm.Expiry, - IncludeLowerLetters: wm.IncludeLowerLetters, - IncludeUpperLetters: wm.IncludeUpperLetters, - IncludeDigits: wm.IncludeDigits, - IncludeSymbols: wm.IncludeSymbols, - }, nil -} From f72560291e9f0910d3e4d98dd6645fbec9377793 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 6 Sep 2023 16:34:07 +0200 Subject: [PATCH 62/76] fix(eventstore): cache instances (#6501) * fix(eventstore): cache instances * fix: consider succeeded once during instance ids query * fix(eventstore): return correct instances (cherry picked from commit 0f06e84f4079150658e0808451cea2b36665c91c) --- internal/eventstore/eventstore.go | 36 +++++++++++++++++-- .../eventstore/handler/handler_projection.go | 2 +- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index 67ab23591..f602a53f1 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -23,6 +23,10 @@ type Eventstore struct { eventTypes []string aggregateTypes []string PushTimeout time.Duration + + instancesMu sync.Mutex + instances []string + lastInstancesQuery time.Time } type eventTypeInterceptors struct { @@ -73,7 +77,16 @@ func (es *Eventstore) Push(ctx context.Context, cmds ...Command) ([]Event, error } func (es *Eventstore) NewInstance(ctx context.Context, instanceID string) error { - return es.repo.CreateInstance(ctx, instanceID) + err := es.repo.CreateInstance(ctx, instanceID) + if err != nil { + return err + } + + es.instancesMu.Lock() + es.instances = append(es.instances, instanceID) + es.instancesMu.Unlock() + + return nil } func (es *Eventstore) EventTypes() []string { @@ -208,12 +221,29 @@ func (es *Eventstore) LatestSequence(ctx context.Context, queryFactory *SearchQu } // InstanceIDs returns the instance ids found by the search query -func (es *Eventstore) InstanceIDs(ctx context.Context, queryFactory *SearchQueryBuilder) ([]string, error) { +// forceDBCall forces to query the database, the instance ids are not cached +func (es *Eventstore) InstanceIDs(ctx context.Context, maxAge time.Duration, forceDBCall bool, queryFactory *SearchQueryBuilder) ([]string, error) { + es.instancesMu.Lock() + defer es.instancesMu.Unlock() + + if !forceDBCall && time.Since(es.lastInstancesQuery) <= maxAge { + return es.instances, nil + } + query, err := queryFactory.build(authz.GetInstance(ctx).InstanceID()) if err != nil { return nil, err } - return es.repo.InstanceIDs(ctx, query) + instances, err := es.repo.InstanceIDs(ctx, query) + if err != nil { + return nil, err + } + if !forceDBCall { + es.instances = instances + es.lastInstancesQuery = time.Now() + } + + return instances, nil } type QueryReducer interface { diff --git a/internal/eventstore/handler/handler_projection.go b/internal/eventstore/handler/handler_projection.go index dcd24b3d7..2dbcfc9aa 100644 --- a/internal/eventstore/handler/handler_projection.go +++ b/internal/eventstore/handler/handler_projection.go @@ -279,7 +279,7 @@ func (h *ProjectionHandler) schedule(ctx context.Context) { // This ensures that only instances with recent events on the handler are projected query = query.CreationDateAfter(h.nowFunc().Add(-1 * h.handleActiveInstances)) } - ids, err := h.Eventstore.InstanceIDs(ctx, query.Builder()) + ids, err := h.Eventstore.InstanceIDs(ctx, h.requeueAfter, !succeededOnce, query.Builder()) if err != nil { logging.WithFields("projection", h.ProjectionName).WithError(err).Error("instance ids") h.triggerProjection.Reset(h.requeueAfter) From 2d7557686401d96fcdfd34f9377b6d2537a7867f Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 7 Sep 2023 10:59:44 +0200 Subject: [PATCH 63/76] fix(scheduler): add span to trigger method (#6513) --- internal/eventstore/handler/handler_projection.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/eventstore/handler/handler_projection.go b/internal/eventstore/handler/handler_projection.go index 2dbcfc9aa..382a05b12 100644 --- a/internal/eventstore/handler/handler_projection.go +++ b/internal/eventstore/handler/handler_projection.go @@ -13,6 +13,7 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/pseudo" + "github.com/zitadel/zitadel/internal/telemetry/tracing" ) const ( @@ -141,6 +142,9 @@ func (h *ProjectionHandler) Trigger(ctx context.Context, instances ...string) co // If a bulk action was executed, the call timestamp in context will be reset for subsequent queries. // The returned context is never nil. It is either the original context or an updated context. func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) (outCtx context.Context, err error) { + ctx, span := tracing.NewSpan(ctx) + defer span.EndWithError(err) + instances = triggerInstances(ctx, instances) defer func() { outCtx = call.ResetTimestamp(ctx) From f5060d70d7e510adadba24e609fab9040ac9a326 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Thu, 7 Sep 2023 16:06:11 +0200 Subject: [PATCH 64/76] fix: handle password data correct on user creation (#6515) --- internal/command/user_human_model.go | 19 +------------------ .../eventstore/handler/handler_projection.go | 4 ---- internal/query/projection/user.go | 4 ++-- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/internal/command/user_human_model.go b/internal/command/user_human_model.go index f2b4a4cf2..9d13d01b9 100644 --- a/internal/command/user_human_model.go +++ b/internal/command/user_human_model.go @@ -3,7 +3,6 @@ package command import ( "golang.org/x/text/language" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/user" @@ -34,9 +33,6 @@ type HumanWriteModel struct { Region string StreetAddress string - Secret *crypto.CryptoValue - SecretChangeRequired bool - UserState domain.UserState } @@ -74,8 +70,6 @@ func (wm *HumanWriteModel) Reduce() error { wm.reduceHumanPhoneVerifiedEvent() case *user.HumanPhoneRemovedEvent: wm.reduceHumanPhoneRemovedEvent() - case *user.HumanPasswordChangedEvent: - wm.reduceHumanPasswordChangedEvent(e) case *user.HumanAvatarAddedEvent: wm.Avatar = e.StoreKey case *user.HumanAvatarRemovedEvent: @@ -123,7 +117,6 @@ func (wm *HumanWriteModel) Query() *eventstore.SearchQueryBuilder { user.HumanPhoneRemovedType, user.HumanAvatarAddedType, user.HumanAvatarRemovedType, - user.HumanPasswordChangedType, user.UserLockedType, user.UserUnlockedType, user.UserDeactivatedType, @@ -138,8 +131,7 @@ func (wm *HumanWriteModel) Query() *eventstore.SearchQueryBuilder { user.UserV1EmailVerifiedType, user.UserV1PhoneChangedType, user.UserV1PhoneVerifiedType, - user.UserV1PhoneRemovedType, - user.UserV1PasswordChangedType). + user.UserV1PhoneRemovedType). Builder() } @@ -158,8 +150,6 @@ func (wm *HumanWriteModel) reduceHumanAddedEvent(e *user.HumanAddedEvent) { wm.PostalCode = e.PostalCode wm.Region = e.Region wm.StreetAddress = e.StreetAddress - wm.Secret = e.Secret - wm.SecretChangeRequired = e.ChangeRequired wm.UserState = domain.UserStateActive } @@ -178,8 +168,6 @@ func (wm *HumanWriteModel) reduceHumanRegisteredEvent(e *user.HumanRegisteredEve wm.PostalCode = e.PostalCode wm.Region = e.Region wm.StreetAddress = e.StreetAddress - wm.Secret = e.Secret - wm.SecretChangeRequired = e.ChangeRequired wm.UserState = domain.UserStateActive } @@ -244,8 +232,3 @@ func (wm *HumanWriteModel) reduceHumanAddressChangedEvent(e *user.HumanAddressCh wm.StreetAddress = *e.StreetAddress } } - -func (wm *HumanWriteModel) reduceHumanPasswordChangedEvent(e *user.HumanPasswordChangedEvent) { - wm.Secret = e.Secret - wm.SecretChangeRequired = e.ChangeRequired -} diff --git a/internal/eventstore/handler/handler_projection.go b/internal/eventstore/handler/handler_projection.go index 382a05b12..2dbcfc9aa 100644 --- a/internal/eventstore/handler/handler_projection.go +++ b/internal/eventstore/handler/handler_projection.go @@ -13,7 +13,6 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/pseudo" - "github.com/zitadel/zitadel/internal/telemetry/tracing" ) const ( @@ -142,9 +141,6 @@ func (h *ProjectionHandler) Trigger(ctx context.Context, instances ...string) co // If a bulk action was executed, the call timestamp in context will be reset for subsequent queries. // The returned context is never nil. It is either the original context or an updated context. func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) (outCtx context.Context, err error) { - ctx, span := tracing.NewSpan(ctx) - defer span.EndWithError(err) - instances = triggerInstances(ctx, instances) defer func() { outCtx = call.ResetTimestamp(ctx) diff --git a/internal/query/projection/user.go b/internal/query/projection/user.go index 64500fa29..38eed7a71 100644 --- a/internal/query/projection/user.go +++ b/internal/query/projection/user.go @@ -352,7 +352,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID), handler.NewCol(NotifyLastEmailCol, e.EmailAddress), handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), - handler.NewCol(NotifyPasswordSetCol, e.Secret != nil), + handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""), }, crdb.WithTableSuffix(UserNotifySuffix), ), @@ -400,7 +400,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID), handler.NewCol(NotifyLastEmailCol, e.EmailAddress), handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), - handler.NewCol(NotifyPasswordSetCol, e.Secret != nil), + handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""), }, crdb.WithTableSuffix(UserNotifySuffix), ), From e64428b8d43f70fa1791e4c300a892898b997e66 Mon Sep 17 00:00:00 2001 From: Silvan Date: Fri, 8 Sep 2023 12:27:52 +0200 Subject: [PATCH 65/76] fix: TestCRDB_CreateInstance (#6522) (cherry picked from commit 856d0d12e879f2ea4a9505ec5b8a1e1ce2de14f8) --- internal/eventstore/repository/sql/crdb_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/eventstore/repository/sql/crdb_test.go b/internal/eventstore/repository/sql/crdb_test.go index 80e347213..0d8b0ca32 100644 --- a/internal/eventstore/repository/sql/crdb_test.go +++ b/internal/eventstore/repository/sql/crdb_test.go @@ -651,7 +651,7 @@ func TestCRDB_CreateInstance(t *testing.T) { t.Errorf("CRDB.CreateInstance() error = %v, wantErr %v", err, tt.res.wantErr) } - sequenceRow := testCRDBClient.QueryRow("SELECT EXISTS(SELECT 1 FROM [SHOW SEQUENCES FROM eventstore] WHERE sequence_name like $1)", "i_"+tt.args.instanceID+"%") + sequenceRow := testCRDBClient.QueryRow("SELECT EXISTS(SELECT 1 FROM [SHOW SEQUENCES] WHERE sequence_schema = 'eventstore' AND sequence_name like $1)", "i_"+tt.args.instanceID+"%") var exists bool err := sequenceRow.Scan(&exists) if err != nil { From 5823fdbef92511e2848855a8ce244e089bb1acde Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Fri, 15 Sep 2023 16:58:45 +0200 Subject: [PATCH 66/76] perf: project quotas and usages (#6441) * project quota added * project quota removed * add periods table * make log record generic * accumulate usage * query usage * count action run seconds * fix filter in ReportQuotaUsage * fix existing tests * fix logstore tests * fix typo * fix: add quota unit tests command side * fix: add quota unit tests command side * fix: add quota unit tests command side * move notifications into debouncer and improve limit querying * cleanup * comment * fix: add quota unit tests command side * fix remaining quota usage query * implement InmemLogStorage * cleanup and linting * improve test * fix: add quota unit tests command side * fix: add quota unit tests command side * fix: add quota unit tests command side * fix: add quota unit tests command side * action notifications and fixes for notifications query * revert console prefix * fix: add quota unit tests command side * fix: add quota integration tests * improve accountable requests * improve accountable requests * fix: add quota integration tests * fix: add quota integration tests * fix: add quota integration tests * comment * remove ability to store logs in db and other changes requested from review * changes requested from review * changes requested from review * Update internal/api/http/middleware/access_interceptor.go Co-authored-by: Silvan * tests: fix quotas integration tests * improve incrementUsageStatement * linting * fix: delete e2e tests as intergation tests cover functionality * Update internal/api/http/middleware/access_interceptor.go Co-authored-by: Silvan * backup * fix conflict * create rc * create prerelease * remove issue release labeling * fix tracing --------- Co-authored-by: Livio Spring Co-authored-by: Stefan Benz Co-authored-by: adlerhurst (cherry picked from commit 1a49b7d298690ce64846727f1fcf5a325f77c76e) --- cmd/defaults.yaml | 47 +- cmd/start/config.go | 6 +- cmd/start/start.go | 31 +- e2e/config/localhost/zitadel.yaml | 23 +- e2e/cypress/e2e/quotas/quotas.cy.ts | 324 --------- internal/actions/actions_test.go | 4 +- internal/actions/fields_test.go | 4 +- internal/actions/http_module_test.go | 4 +- internal/actions/log_module.go | 15 +- .../server/middleware/access_interceptor.go | 10 +- .../server/middleware/quota_interceptor.go | 17 +- internal/api/grpc/server/server.go | 7 +- .../api/grpc/system/quota_integration_test.go | 203 ++++++ .../grpc/system/server_integration_test.go | 32 + .../api/http/middleware/access_interceptor.go | 106 +-- internal/api/oidc/op.go | 39 +- internal/api/saml/provider.go | 25 +- internal/command/instance.go | 5 +- internal/command/main_test.go | 2 + internal/command/quota.go | 45 +- internal/command/quota_model.go | 1 + internal/command/quota_report.go | 43 +- internal/command/quota_report_test.go | 307 +++++++++ internal/command/quota_test.go | 638 ++++++++++++++++++ internal/database/type.go | 13 + internal/integration/assert.go | 4 +- internal/integration/config/zitadel.yaml | 11 +- internal/integration/integration.go | 77 ++- internal/logstore/config.go | 7 +- internal/logstore/debouncer.go | 30 +- internal/logstore/emitter.go | 72 +- internal/logstore/emitters/access/database.go | 196 ++---- internal/logstore/emitters/access/record.go | 97 --- .../logstore/emitters/execution/database.go | 166 ++--- internal/logstore/emitters/mock/inmem.go | 89 --- internal/logstore/emitters/stdout/stdout.go | 4 +- internal/logstore/helpers_test.go | 25 +- internal/logstore/mock/inmem.go | 120 ++++ .../logstore/{emitters => }/mock/record.go | 10 +- internal/logstore/quotaqueriers/mock/noop.go | 28 - internal/logstore/record/access.go | 135 ++++ .../record_test.go => record/access_test.go} | 25 +- .../record.go => record/execution.go} | 17 +- internal/logstore/record/prune.go | 8 + internal/logstore/service.go | 100 +-- internal/logstore/service_test.go | 48 +- {cmd/start => internal/net}/start_port.go | 4 +- .../net}/start_port_integration.go | 4 +- .../handlers/handlers_integration_test.go | 6 +- .../telemetry_pusher_integration_test.go | 35 +- internal/query/projection/main_test.go | 2 + internal/query/projection/projection.go | 3 + internal/query/projection/quota.go | 285 ++++++++ internal/query/projection/quota_test.go | 321 +++++++++ internal/query/query.go | 2 + internal/query/quota.go | 121 ++++ internal/query/quota_model.go | 55 -- internal/query/quota_notifications.go | 196 +++++- internal/query/quota_notifications_model.go | 46 -- internal/query/quota_notifications_test.go | 181 +++++ internal/query/quota_period.go | 30 - internal/query/quota_periods.go | 86 +++ internal/query/quota_periods_test.go | 95 +++ internal/query/quota_test.go | 127 ++++ internal/repository/quota/aggregate.go | 4 +- internal/repository/quota/events.go | 13 +- 66 files changed, 3423 insertions(+), 1413 deletions(-) delete mode 100644 e2e/cypress/e2e/quotas/quotas.cy.ts create mode 100644 internal/api/grpc/system/quota_integration_test.go create mode 100644 internal/api/grpc/system/server_integration_test.go create mode 100644 internal/command/quota_report_test.go create mode 100644 internal/command/quota_test.go delete mode 100644 internal/logstore/emitters/access/record.go delete mode 100644 internal/logstore/emitters/mock/inmem.go create mode 100644 internal/logstore/mock/inmem.go rename internal/logstore/{emitters => }/mock/record.go (50%) delete mode 100644 internal/logstore/quotaqueriers/mock/noop.go create mode 100644 internal/logstore/record/access.go rename internal/logstore/{emitters/access/record_test.go => record/access_test.go} (85%) rename internal/logstore/{emitters/execution/record.go => record/execution.go} (63%) create mode 100644 internal/logstore/record/prune.go rename {cmd/start => internal/net}/start_port.go (58%) rename {cmd/start => internal/net}/start_port_integration.go (87%) create mode 100644 internal/query/projection/quota.go create mode 100644 internal/query/projection/quota_test.go create mode 100644 internal/query/quota.go delete mode 100644 internal/query/quota_model.go delete mode 100644 internal/query/quota_notifications_model.go create mode 100644 internal/query/quota_notifications_test.go delete mode 100644 internal/query/quota_period.go create mode 100644 internal/query/quota_periods.go create mode 100644 internal/query/quota_periods_test.go create mode 100644 internal/query/quota_test.go diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index debcd9051..c95170e2b 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -452,54 +452,29 @@ Actions: LogStore: Access: - Database: - # If enabled, all access logs are stored in the database table logstore.access - Enabled: false # ZITADEL_LOGSTORE_ACCESS_DATABASE_ENABLED - # Logs that are older than the keep duration are cleaned up continuously - # 2160h are 90 days, 3 months - Keep: 2160h # ZITADEL_LOGSTORE_ACCESS_DATABASE_KEEP - # CleanupInterval defines the time between cleanup iterations - CleanupInterval: 4h # ZITADEL_LOGSTORE_ACCESS_DATABASE_CLEANUPINTERVAL - # Debouncing enables to asynchronously emit log entries, so the normal execution performance is not impaired - # Log entries are held in memory until one of the conditions MinFrequency or MaxBulkSize meets. - Debounce: - MinFrequency: 2m # ZITADEL_LOGSTORE_ACCESS_DATABASE_DEBOUNCE_MINFREQUENCY - MaxBulkSize: 100 # ZITADEL_LOGSTORE_ACCESS_DATABASE_DEBOUNCE_MAXBULKSIZE Stdout: # If enabled, all access logs are printed to the binary's standard output Enabled: false # ZITADEL_LOGSTORE_ACCESS_STDOUT_ENABLED - # Debouncing enables to asynchronously emit log entries, so the normal execution performance is not impaired - # Log entries are held in memory until one of the conditions MinFrequency or MaxBulkSize meets. - Debounce: - MinFrequency: 0s # ZITADEL_LOGSTORE_ACCESS_STDOUT_DEBOUNCE_MINFREQUENCY - MaxBulkSize: 0 # ZITADEL_LOGSTORE_ACCESS_STDOUT_DEBOUNCE_MAXBULKSIZE Execution: - Database: - # If enabled, all action execution logs are stored in the database table logstore.execution - Enabled: false # ZITADEL_LOGSTORE_EXECUTION_DATABASE_ENABLED - # Logs that are older than the keep duration are cleaned up continuously - # 2160h are 90 days, 3 months - Keep: 2160h # ZITADEL_LOGSTORE_EXECUTION_DATABASE_KEEP - # CleanupInterval defines the time between cleanup iterations - CleanupInterval: 4h # ZITADEL_LOGSTORE_EXECUTION_DATABASE_CLEANUPINTERVAL - # Debouncing enables to asynchronously emit log entries, so the normal execution performance is not impaired - # Log entries are held in memory until one of the conditions MinFrequency or MaxBulkSize meets. - Debounce: - MinFrequency: 0s # ZITADEL_LOGSTORE_EXECUTION_DATABASE_DEBOUNCE_MINFREQUENCY - MaxBulkSize: 0 # ZITADEL_LOGSTORE_EXECUTION_DATABASE_DEBOUNCE_MAXBULKSIZE Stdout: # If enabled, all execution logs are printed to the binary's standard output Enabled: true # ZITADEL_LOGSTORE_EXECUTION_STDOUT_ENABLED - # Debouncing enables to asynchronously emit log entries, so the normal execution performance is not impaired - # Log entries are held in memory until one of the conditions MinFrequency or MaxBulkSize meets. - Debounce: - MinFrequency: 0s # ZITADEL_LOGSTORE_EXECUTION_STDOUT_DEBOUNCE_MINFREQUENCY - MaxBulkSize: 0 # ZITADEL_LOGSTORE_EXECUTION_STDOUT_DEBOUNCE_MAXBULKSIZE Quotas: Access: + # If enabled, authenticated requests are counted and potentially limited depending on the configured quota of the instance + Enabled: false # ZITADEL_QUOTAS_ACCESS_ENABLED + Debounce: + MinFrequency: 0s # ZITADEL_QUOTAS_ACCESS_DEBOUNCE_MINFREQUENCY + MaxBulkSize: 0 # ZITADEL_QUOTAS_ACCESS_DEBOUNCE_MAXBULKSIZE ExhaustedCookieKey: "zitadel.quota.exhausted" # ZITADEL_QUOTAS_ACCESS_EXHAUSTEDCOOKIEKEY ExhaustedCookieMaxAge: "300s" # ZITADEL_QUOTAS_ACCESS_EXHAUSTEDCOOKIEMAXAGE + Execution: + # If enabled, all action executions are counted and potentially limited depending on the configured quota of the instance + Enabled: false # ZITADEL_QUOTAS_EXECUTION_DATABASE_ENABLED + Debounce: + MinFrequency: 0s # ZITADEL_QUOTAS_EXECUTION_DEBOUNCE_MINFREQUENCY + MaxBulkSize: 0 # ZITADEL_QUOTAS_EXECUTION_DEBOUNCE_MAXBULKSIZE Eventstore: PushTimeout: 15s # ZITADEL_EVENTSTORE_PUSHTIMEOUT diff --git a/cmd/start/config.go b/cmd/start/config.go index 0739664aa..6280fdea4 100644 --- a/cmd/start/config.go +++ b/cmd/start/config.go @@ -72,7 +72,11 @@ type Config struct { } type QuotasConfig struct { - Access *middleware.AccessConfig + Access struct { + logstore.EmitterConfig `mapstructure:",squash"` + middleware.AccessConfig `mapstructure:",squash"` + } + Execution *logstore.EmitterConfig } func MustNewConfig(v *viper.Viper) *Config { diff --git a/cmd/start/start.go b/cmd/start/start.go index f8d69dea5..b7eb8e59c 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -64,6 +64,8 @@ import ( "github.com/zitadel/zitadel/internal/logstore/emitters/access" "github.com/zitadel/zitadel/internal/logstore/emitters/execution" "github.com/zitadel/zitadel/internal/logstore/emitters/stdout" + "github.com/zitadel/zitadel/internal/logstore/record" + "github.com/zitadel/zitadel/internal/net" "github.com/zitadel/zitadel/internal/notification" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/static" @@ -108,7 +110,6 @@ type Server struct { AuthzRepo authz_repo.Repository Storage static.Storage Commands *command.Commands - LogStore *logstore.Service Router *mux.Router TLSConfig *tls.Config Shutdown chan<- os.Signal @@ -209,17 +210,16 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error } clock := clockpkg.New() - actionsExecutionStdoutEmitter, err := logstore.NewEmitter(ctx, clock, config.LogStore.Execution.Stdout, stdout.NewStdoutEmitter()) + actionsExecutionStdoutEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, &logstore.EmitterConfig{Enabled: config.LogStore.Execution.Stdout.Enabled}, stdout.NewStdoutEmitter[*record.ExecutionLog]()) if err != nil { return err } - actionsExecutionDBEmitter, err := logstore.NewEmitter(ctx, clock, config.LogStore.Execution.Database, execution.NewDatabaseLogStorage(dbClient)) + actionsExecutionDBEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, config.Quotas.Execution, execution.NewDatabaseLogStorage(dbClient, commands, queries)) if err != nil { return err } - usageReporter := logstore.UsageReporterFunc(commands.ReportQuotaUsage) - actionsLogstoreSvc := logstore.New(queries, usageReporter, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter) + actionsLogstoreSvc := logstore.New(queries, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter) actions.SetLogstoreService(actionsLogstoreSvc) notification.Start( @@ -259,8 +259,6 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error storage, authZRepo, keys, - queries, - usageReporter, permissionCheck, ) if err != nil { @@ -281,7 +279,6 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error AuthzRepo: authZRepo, Storage: storage, Commands: commands, - LogStore: actionsLogstoreSvc, Router: router, TLSConfig: tlsConfig, Shutdown: shutdown, @@ -304,8 +301,6 @@ func startAPIs( store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys, - quotaQuerier logstore.QuotaQuerier, - usageReporter logstore.UsageReporter, permissionCheck domain.PermissionCheck, ) error { repo := struct { @@ -321,22 +316,22 @@ func startAPIs( return err } - accessStdoutEmitter, err := logstore.NewEmitter(ctx, clock, config.LogStore.Access.Stdout, stdout.NewStdoutEmitter()) + accessStdoutEmitter, err := logstore.NewEmitter[*record.AccessLog](ctx, clock, &logstore.EmitterConfig{Enabled: config.LogStore.Access.Stdout.Enabled}, stdout.NewStdoutEmitter[*record.AccessLog]()) if err != nil { return err } - accessDBEmitter, err := logstore.NewEmitter(ctx, clock, config.LogStore.Access.Database, access.NewDatabaseLogStorage(dbClient)) + accessDBEmitter, err := logstore.NewEmitter[*record.AccessLog](ctx, clock, &config.Quotas.Access.EmitterConfig, access.NewDatabaseLogStorage(dbClient, commands, queries)) if err != nil { return err } - accessSvc := logstore.New(quotaQuerier, usageReporter, accessDBEmitter, accessStdoutEmitter) + accessSvc := logstore.New[*record.AccessLog](queries, accessDBEmitter, accessStdoutEmitter) exhaustedCookieHandler := http_util.NewCookieHandler( http_util.WithUnsecure(), http_util.WithNonHttpOnly(), http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))), ) - limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, config.Quotas.Access) + limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig) apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, limitingAccessInterceptor) if err != nil { return fmt.Errorf("error creating api %w", err) @@ -399,13 +394,13 @@ func startAPIs( } apis.RegisterHandlerOnPrefix(openapi.HandlerPrefix, openAPIHandler) - oidcProvider, err := oidc.NewProvider(config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, limitingAccessInterceptor.Handle) + oidcProvider, err := oidc.NewProvider(config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, limitingAccessInterceptor) if err != nil { return fmt.Errorf("unable to start oidc provider: %w", err) } apis.RegisterHandlerPrefixes(oidcProvider.HttpHandler(), "/.well-known/openid-configuration", "/oidc/v1", "/oauth/v2") - samlProvider, err := saml.NewProvider(config.SAML, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.SAML, eventstore, dbClient, instanceInterceptor.Handler, userAgentInterceptor, limitingAccessInterceptor.Handle) + samlProvider, err := saml.NewProvider(config.SAML, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.SAML, eventstore, dbClient, instanceInterceptor.Handler, userAgentInterceptor, limitingAccessInterceptor) if err != nil { return fmt.Errorf("unable to start saml provider: %w", err) } @@ -417,7 +412,7 @@ func startAPIs( } apis.RegisterHandlerOnPrefix(console.HandlerPrefix, c) - l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), provider.AuthCallbackURL(samlProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, provider.NewIssuerInterceptor(samlProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle, keys.User, keys.IDPConfig, keys.CSRFCookieKey) + l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), provider.AuthCallbackURL(samlProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, provider.NewIssuerInterceptor(samlProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.WithoutLimiting().Handle, keys.User, keys.IDPConfig, keys.CSRFCookieKey) if err != nil { return fmt.Errorf("unable to start login: %w", err) } @@ -438,7 +433,7 @@ func listen(ctx context.Context, router *mux.Router, port uint16, tlsConfig *tls http2Server := &http2.Server{} http1Server := &http.Server{Handler: h2c.NewHandler(router, http2Server), TLSConfig: tlsConfig} - lc := listenConfig() + lc := net.ListenConfig() lis, err := lc.Listen(ctx, "tcp", fmt.Sprintf(":%d", port)) if err != nil { return fmt.Errorf("tcp listener on %d failed: %w", port, err) diff --git a/e2e/config/localhost/zitadel.yaml b/e2e/config/localhost/zitadel.yaml index e53061218..de758680f 100644 --- a/e2e/config/localhost/zitadel.yaml +++ b/e2e/config/localhost/zitadel.yaml @@ -28,12 +28,7 @@ LogStore: Database: Enabled: true Stdout: - Enabled: false - -Quotas: - Access: - ExhaustedCookieKey: "zitadel.quota.limiting" - ExhaustedCookieMaxAge: "600s" + Enabled: true Console: InstanceManagementURL: "https://example.com/instances/{{.InstanceID}}" @@ -43,9 +38,25 @@ Projections: NotificationsQuotas: RequeueEvery: 1s +Quotas: + Access: + ExhaustedCookieKey: "zitadel.quota.limiting" + ExhaustedCookieMaxAge: "600s" + DefaultInstance: LoginPolicy: MfaInitSkipLifetime: "0" + Quotas: + Items: + - Unit: "actions.all.runs.seconds" + From: "2023-01-01T00:00:00Z" + ResetInterval: 5m + Amount: 20 + Limit: false + Notifications: + - Percent: 100 + Repeat: true + CallURL: "https://httpbin.org/post" SystemAPIUsers: - cypress: diff --git a/e2e/cypress/e2e/quotas/quotas.cy.ts b/e2e/cypress/e2e/quotas/quotas.cy.ts deleted file mode 100644 index a8a3046ce..000000000 --- a/e2e/cypress/e2e/quotas/quotas.cy.ts +++ /dev/null @@ -1,324 +0,0 @@ -import { addQuota, ensureQuotaIsAdded, ensureQuotaIsRemoved, removeQuota, Unit } from 'support/api/quota'; -import { createHumanUser, ensureUserDoesntExist } from 'support/api/users'; -import { Context } from 'support/commands'; -import { ZITADELWebhookEvent } from 'support/types'; -import { textChangeRangeIsUnchanged } from 'typescript'; - -beforeEach(() => { - cy.context().as('ctx'); -}); - -describe('quotas', () => { - describe('management', () => { - describe('add one quota', () => { - it('should add a quota only once per unit', () => { - cy.get('@ctx').then((ctx) => { - addQuota(ctx, Unit.AuthenticatedRequests, true, 1); - addQuota(ctx, Unit.AuthenticatedRequests, true, 1, undefined, undefined, undefined, false).then((res) => { - expect(res.status).to.equal(409); - }); - }); - }); - - describe('add two quotas', () => { - it('should add a quota for each unit', () => { - cy.get('@ctx').then((ctx) => { - addQuota(ctx, Unit.AuthenticatedRequests, true, 1); - addQuota(ctx, Unit.ExecutionSeconds, true, 1); - }); - }); - }); - }); - - describe('edit', () => { - describe('remove one quota', () => { - beforeEach(() => { - cy.get('@ctx').then((ctx) => { - ensureQuotaIsAdded(ctx, Unit.AuthenticatedRequests, true, 1); - }); - }); - it('should remove a quota only once per unit', () => { - cy.get('@ctx').then((ctx) => { - removeQuota(ctx, Unit.AuthenticatedRequests); - }); - cy.get('@ctx').then((ctx) => { - removeQuota(ctx, Unit.AuthenticatedRequests, false).then((res) => { - expect(res.status).to.equal(404); - }); - }); - }); - - describe('remove two quotas', () => { - beforeEach(() => { - cy.get('@ctx').then((ctx) => { - ensureQuotaIsAdded(ctx, Unit.AuthenticatedRequests, true, 1); - ensureQuotaIsAdded(ctx, Unit.ExecutionSeconds, true, 1); - }); - }); - it('should remove a quota for each unit', () => { - cy.get('@ctx').then((ctx) => { - removeQuota(ctx, Unit.AuthenticatedRequests); - removeQuota(ctx, Unit.ExecutionSeconds); - }); - }); - }); - }); - }); - }); - - describe('usage', () => { - beforeEach(() => { - cy.get('@ctx') - .then((ctx) => { - return [ - `${ctx.api.oidcBaseURL}/userinfo`, - `${ctx.api.authBaseURL}/users/me`, - `${ctx.api.mgmtBaseURL}/iam`, - `${ctx.api.adminBaseURL}/instances/me`, - `${ctx.api.oauthBaseURL}/keys`, - `${ctx.api.samlBaseURL}/certificate`, - ]; - }) - .as('authenticatedUrls'); - }); - - describe('authenticated requests', () => { - const testUserName = 'shouldNotBeCreated'; - beforeEach(() => { - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - ensureUserDoesntExist(ctx.api, testUserName); - ensureQuotaIsAdded(ctx, Unit.AuthenticatedRequests, true, urls.length); - cy.task('runSQL', `TRUNCATE logstore.access;`); - }); - }); - }); - - it('only authenticated requests are limited', () => { - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - const start = new Date(); - urls.forEach((url) => { - cy.request({ - url: url, - method: 'GET', - auth: { - bearer: ctx.api.token, - }, - }); - }); - expectCookieDoesntExist(); - const expiresMax = new Date(); - expiresMax.setMinutes(expiresMax.getMinutes() + 20); - cy.request({ - url: urls[1], - method: 'GET', - auth: { - bearer: ctx.api.token, - }, - failOnStatusCode: false, - }).then((res) => { - expect(res.status).to.equal(429); - }); - cy.getCookie('zitadel.quota.limiting').then((cookie) => { - expect(cookie.value).to.equal('true'); - const cookieExpiry = new Date(); - cookieExpiry.setTime(cookie.expiry * 1000); - expect(cookieExpiry).to.be.within(start, expiresMax); - }); - createHumanUser(ctx.api, testUserName, false).then((res) => { - expect(res.status).to.equal(429); - }); - // visit limited console - // cy.visit('/users/me'); - // cy.contains('#authenticated-requests-exhausted-dialog button', 'Continue').click(); - // const upgradeInstancePage = `https://example.com/instances/${ctx.instanceId}`; - // cy.origin(upgradeInstancePage, { args: { upgradeInstancePage } }, ({ upgradeInstancePage }) => { - // cy.location('href').should('equal', upgradeInstancePage); - // }); - // upgrade instance - ensureQuotaIsRemoved(ctx, Unit.AuthenticatedRequests); - // visit upgraded console again - cy.visit('/users/me'); - cy.get('[data-e2e="top-view-title"]'); - expectCookieDoesntExist(); - createHumanUser(ctx.api, testUserName); - expectCookieDoesntExist(); - }); - }); - }); - }); - - describe.skip('notifications', () => { - const callURL = `http://${Cypress.env('WEBHOOK_HANDLER_HOST')}:${Cypress.env('WEBHOOK_HANDLER_PORT')}/do_something`; - - beforeEach(() => cy.task('resetWebhookEvents')); - - const amount = 100; - const percent = 10; - const usage = 35; - - describe('without repetition', () => { - beforeEach(() => { - cy.get('@ctx').then((ctx) => { - ensureQuotaIsAdded(ctx, Unit.AuthenticatedRequests, false, amount, [ - { - callUrl: callURL, - percent: percent, - repeat: false, - }, - ]); - cy.task('runSQL', `TRUNCATE logstore.access;`); - }); - }); - - it('fires at least once with the expected payload', () => { - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - for (let i = 0; i < usage; i++) { - cy.request({ - url: urls[0], - method: 'GET', - auth: { - bearer: ctx.api.token, - }, - }); - } - }); - cy.waitUntil( - () => - cy.task>('handledWebhookEvents').then((events) => { - if (events.length < 1) { - return false; - } - return Cypress._.matches({ - sentStatus: 200, - payload: { - callURL: callURL, - threshold: percent, - unit: 1, - usage: percent, - }, - })(events[0]); - }), - { timeout: 60_000 }, - ); - }); - }); - - it('fires until the webhook returns a successful message', () => { - cy.task('failWebhookEvents', 8); - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - for (let i = 0; i < usage; i++) { - cy.request({ - url: urls[0], - method: 'GET', - auth: { - bearer: ctx.api.token, - }, - }); - } - }); - cy.waitUntil( - () => - cy.task>('handledWebhookEvents').then((events) => { - if (events.length != 9) { - return false; - } - return events.reduce((a, b, i) => { - return !a - ? a - : i < 8 - ? Cypress._.matches({ - sentStatus: 500, - payload: { - callURL: callURL, - threshold: percent, - unit: 1, - usage: percent, - }, - })(b) - : Cypress._.matches({ - sentStatus: 200, - payload: { - callURL: callURL, - threshold: percent, - unit: 1, - usage: percent, - }, - })(b); - }, true); - }), - { timeout: 60_000 }, - ); - }); - }); - }); - - describe('with repetition', () => { - beforeEach(() => { - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - ensureQuotaIsAdded(ctx, Unit.AuthenticatedRequests, false, amount, [ - { - callUrl: callURL, - percent: percent, - repeat: true, - }, - ]); - cy.task('runSQL', `TRUNCATE logstore.access;`); - }); - }); - }); - - it('fires repeatedly with the expected payloads', () => { - cy.get>('@authenticatedUrls').then((urls) => { - cy.get('@ctx').then((ctx) => { - for (let i = 0; i < usage; i++) { - cy.request({ - url: urls[0], - method: 'GET', - auth: { - bearer: ctx.api.token, - }, - }); - } - }); - }); - cy.waitUntil( - () => - cy.task>('handledWebhookEvents').then((events) => { - let foundExpected = 0; - for (let i = 0; i < events.length; i++) { - for (let expect = 10; expect <= 30; expect += 10) { - if ( - Cypress._.matches({ - sentStatus: 200, - payload: { - callURL: callURL, - threshold: expect, - unit: 1, - usage: expect, - }, - })(events[i]) - ) { - foundExpected++; - } - } - } - return foundExpected >= 3; - }), - { timeout: 60_000 }, - ); - }); - }); - }); - }); -}); - -function expectCookieDoesntExist() { - cy.getCookie('zitadel.quota.limiting').then((cookie) => { - expect(cookie).to.be.null; - }); -} diff --git a/internal/actions/actions_test.go b/internal/actions/actions_test.go index 1287546a9..d33b3082a 100644 --- a/internal/actions/actions_test.go +++ b/internal/actions/actions_test.go @@ -7,11 +7,13 @@ import ( "time" "github.com/dop251/goja" + "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" ) func TestRun(t *testing.T) { - SetLogstoreService(logstore.New(nil, nil, nil)) + SetLogstoreService(logstore.New[*record.ExecutionLog](nil, nil)) type args struct { timeout time.Duration api apiFields diff --git a/internal/actions/fields_test.go b/internal/actions/fields_test.go index 93862cfb3..9f1f8e44d 100644 --- a/internal/actions/fields_test.go +++ b/internal/actions/fields_test.go @@ -5,11 +5,13 @@ import ( "testing" "github.com/dop251/goja" + "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" ) func TestSetFields(t *testing.T) { - SetLogstoreService(logstore.New(nil, nil, nil)) + SetLogstoreService(logstore.New[*record.ExecutionLog](nil, nil)) primitveFn := func(a string) { fmt.Println(a) } complexFn := func(*FieldConfig) interface{} { return primitveFn diff --git a/internal/actions/http_module_test.go b/internal/actions/http_module_test.go index 176f789e2..1b456d219 100644 --- a/internal/actions/http_module_test.go +++ b/internal/actions/http_module_test.go @@ -10,12 +10,14 @@ import ( "testing" "github.com/dop251/goja" + "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" ) func Test_isHostBlocked(t *testing.T) { - SetLogstoreService(logstore.New(nil, nil, nil)) + SetLogstoreService(logstore.New[*record.ExecutionLog](nil, nil)) var denyList = []AddressChecker{ mustNewIPChecker(t, "192.168.5.0/24"), mustNewIPChecker(t, "127.0.0.1"), diff --git a/internal/actions/log_module.go b/internal/actions/log_module.go index 1d2e28adf..a7a57a80c 100644 --- a/internal/actions/log_module.go +++ b/internal/actions/log_module.go @@ -10,15 +10,15 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/logstore/emitters/execution" + "github.com/zitadel/zitadel/internal/logstore/record" ) var ( - logstoreService *logstore.Service + logstoreService *logstore.Service[*record.ExecutionLog] _ console.Printer = (*logger)(nil) ) -func SetLogstoreService(svc *logstore.Service) { +func SetLogstoreService(svc *logstore.Service[*record.ExecutionLog]) { logstoreService = svc } @@ -55,19 +55,16 @@ func (l *logger) log(msg string, level logrus.Level, last bool) { if l.started.IsZero() { l.started = ts } - - record := &execution.Record{ + r := &record.ExecutionLog{ LogDate: ts, InstanceID: l.instanceID, Message: msg, LogLevel: level, } - if last { - record.Took = ts.Sub(l.started) + r.Took = ts.Sub(l.started) } - - logstoreService.Handle(l.ctx, record) + logstoreService.Handle(l.ctx, r) } func withLogger(ctx context.Context) Option { diff --git a/internal/api/grpc/server/middleware/access_interceptor.go b/internal/api/grpc/server/middleware/access_interceptor.go index 2b3cd43df..719e03da7 100644 --- a/internal/api/grpc/server/middleware/access_interceptor.go +++ b/internal/api/grpc/server/middleware/access_interceptor.go @@ -10,11 +10,11 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/logstore/emitters/access" + "github.com/zitadel/zitadel/internal/logstore/record" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func AccessStorageInterceptor(svc *logstore.Service) grpc.UnaryServerInterceptor { +func AccessStorageInterceptor(svc *logstore.Service[*record.AccessLog]) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) { if !svc.Enabled() { return handler(ctx, req) @@ -36,9 +36,9 @@ func AccessStorageInterceptor(svc *logstore.Service) grpc.UnaryServerInterceptor resMd, _ := metadata.FromOutgoingContext(ctx) instance := authz.GetInstance(ctx) - record := &access.Record{ + r := &record.AccessLog{ LogDate: time.Now(), - Protocol: access.GRPC, + Protocol: record.GRPC, RequestURL: info.FullMethod, ResponseStatus: respStatus, RequestHeaders: reqMd, @@ -49,7 +49,7 @@ func AccessStorageInterceptor(svc *logstore.Service) grpc.UnaryServerInterceptor RequestedHost: instance.RequestedHost(), } - svc.Handle(interceptorCtx, record) + svc.Handle(interceptorCtx, r) return resp, handlerErr } } diff --git a/internal/api/grpc/server/middleware/quota_interceptor.go b/internal/api/grpc/server/middleware/quota_interceptor.go index 08ce38907..cfcdcedb9 100644 --- a/internal/api/grpc/server/middleware/quota_interceptor.go +++ b/internal/api/grpc/server/middleware/quota_interceptor.go @@ -9,19 +9,16 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func QuotaExhaustedInterceptor(svc *logstore.Service, ignoreService ...string) grpc.UnaryServerInterceptor { - - prunedIgnoredServices := make([]string, len(ignoreService)) +func QuotaExhaustedInterceptor(svc *logstore.Service[*record.AccessLog], ignoreService ...string) grpc.UnaryServerInterceptor { for idx, service := range ignoreService { if !strings.HasPrefix(service, "/") { - service = "/" + service + ignoreService[idx] = "/" + service } - prunedIgnoredServices[idx] = service } - return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) { if !svc.Enabled() { return handler(ctx, req) @@ -29,7 +26,13 @@ func QuotaExhaustedInterceptor(svc *logstore.Service, ignoreService ...string) g interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx) defer func() { span.EndWithError(err) }() - for _, service := range prunedIgnoredServices { + // The auth interceptor will ensure that only authorized or public requests are allowed. + // So if there's no authorization context, we don't need to check for limitation + if authz.GetCtxData(ctx).IsZero() { + return handler(ctx, req) + } + + for _, service := range ignoreService { if strings.HasPrefix(info.FullMethod, service) { return handler(ctx, req) } diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index 925277803..9d7deb28e 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -12,6 +12,7 @@ import ( grpc_api "github.com/zitadel/zitadel/internal/api/grpc" "github.com/zitadel/zitadel/internal/api/grpc/server/middleware" "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/telemetry/metrics" system_pb "github.com/zitadel/zitadel/pkg/grpc/system" @@ -39,7 +40,7 @@ func CreateServer( queries *query.Queries, hostHeaderName string, tlsConfig *tls.Config, - accessSvc *logstore.Service, + accessSvc *logstore.Service[*record.AccessLog], ) *grpc.Server { metricTypes := []metrics.MetricType{metrics.MetricTypeTotalCount, metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode} serverOptions := []grpc.ServerOption{ @@ -49,14 +50,14 @@ func CreateServer( middleware.DefaultTracingServer(), middleware.MetricsHandler(metricTypes, grpc_api.Probes...), middleware.NoCacheInterceptor(), - middleware.ErrorHandler(), middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName), middleware.AccessStorageInterceptor(accessSvc), + middleware.ErrorHandler(), middleware.AuthorizationInterceptor(verifier, authConfig), + middleware.QuotaExhaustedInterceptor(accessSvc, system_pb.SystemService_ServiceDesc.ServiceName), middleware.TranslationHandler(), middleware.ValidationHandler(), middleware.ServiceHandler(), - middleware.QuotaExhaustedInterceptor(accessSvc, system_pb.SystemService_ServiceDesc.ServiceName), ), ), } diff --git a/internal/api/grpc/system/quota_integration_test.go b/internal/api/grpc/system/quota_integration_test.go new file mode 100644 index 000000000..a8c6840c8 --- /dev/null +++ b/internal/api/grpc/system/quota_integration_test.go @@ -0,0 +1,203 @@ +//go:build integration + +package system_test + +import ( + "bytes" + "encoding/json" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/pkg/grpc/admin" + quota_pb "github.com/zitadel/zitadel/pkg/grpc/quota" + "github.com/zitadel/zitadel/pkg/grpc/system" +) + +var callURL = "http://localhost:" + integration.PortQuotaServer + +func TestServer_QuotaNotification_Limit(t *testing.T) { + _, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX) + amount := 10 + percent := 50 + percentAmount := amount * percent / 100 + + _, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + From: timestamppb.Now(), + ResetInterval: durationpb.New(time.Minute * 5), + Amount: uint64(amount), + Limit: true, + Notifications: []*quota_pb.Notification{ + { + Percent: uint32(percent), + Repeat: true, + CallUrl: callURL, + }, + { + Percent: 100, + Repeat: true, + CallUrl: callURL, + }, + }, + }) + require.NoError(t, err) + + for i := 0; i < percentAmount; i++ { + _, err := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoErrorf(t, err, "error in %d call of %d", i, percentAmount) + } + awaitNotification(t, Tester.QuotaNotificationChan, quota.RequestsAllAuthenticated, percent) + + for i := 0; i < (amount - percentAmount); i++ { + _, err := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoErrorf(t, err, "error in %d call of %d", i, percentAmount) + } + awaitNotification(t, Tester.QuotaNotificationChan, quota.RequestsAllAuthenticated, 100) + + _, limitErr := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.Error(t, limitErr) +} + +func TestServer_QuotaNotification_NoLimit(t *testing.T) { + _, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX) + amount := 10 + percent := 50 + percentAmount := amount * percent / 100 + + _, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + From: timestamppb.Now(), + ResetInterval: durationpb.New(time.Minute * 5), + Amount: uint64(amount), + Limit: false, + Notifications: []*quota_pb.Notification{ + { + Percent: uint32(percent), + Repeat: false, + CallUrl: callURL, + }, + { + Percent: 100, + Repeat: true, + CallUrl: callURL, + }, + }, + }) + require.NoError(t, err) + + for i := 0; i < percentAmount; i++ { + _, err := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoErrorf(t, err, "error in %d call of %d", i, percentAmount) + } + awaitNotification(t, Tester.QuotaNotificationChan, quota.RequestsAllAuthenticated, percent) + + for i := 0; i < (amount - percentAmount); i++ { + _, err := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoErrorf(t, err, "error in %d call of %d", i, percentAmount) + } + awaitNotification(t, Tester.QuotaNotificationChan, quota.RequestsAllAuthenticated, 100) + + for i := 0; i < amount; i++ { + _, err := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoErrorf(t, err, "error in %d call of %d", i, percentAmount) + } + awaitNotification(t, Tester.QuotaNotificationChan, quota.RequestsAllAuthenticated, 200) + + _, limitErr := Tester.Client.Admin.GetDefaultOrg(iamOwnerCtx, &admin.GetDefaultOrgRequest{}) + require.NoError(t, limitErr) +} + +func awaitNotification(t *testing.T, bodies chan []byte, unit quota.Unit, percent int) { + for { + select { + case body := <-bodies: + plain := new(bytes.Buffer) + if err := json.Indent(plain, body, "", " "); err != nil { + t.Fatal(err) + } + t.Log("received notificationDueEvent", plain.String()) + event := struct { + Unit quota.Unit `json:"unit"` + ID string `json:"id"` + CallURL string `json:"callURL"` + PeriodStart time.Time `json:"periodStart"` + Threshold uint16 `json:"threshold"` + Usage uint64 `json:"usage"` + }{} + if err := json.Unmarshal(body, &event); err != nil { + t.Error(err) + } + if event.ID == "" { + continue + } + if event.Unit == unit && event.Threshold == uint16(percent) { + return + } + case <-time.After(60 * time.Second): + t.Fatalf("timed out waiting for unit %s and percent %d", strconv.Itoa(int(unit)), percent) + } + } +} + +func TestServer_AddAndRemoveQuota(t *testing.T) { + _, instanceID, _ := Tester.UseIsolatedInstance(CTX, SystemCTX) + + got, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + From: timestamppb.Now(), + ResetInterval: durationpb.New(time.Minute), + Amount: 10, + Limit: true, + Notifications: []*quota_pb.Notification{ + { + Percent: 20, + Repeat: true, + CallUrl: callURL, + }, + }, + }) + require.NoError(t, err) + require.Equal(t, got.Details.ResourceOwner, instanceID) + + gotAlreadyExisting, errAlreadyExisting := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + From: timestamppb.Now(), + ResetInterval: durationpb.New(time.Minute), + Amount: 10, + Limit: true, + Notifications: []*quota_pb.Notification{ + { + Percent: 20, + Repeat: true, + CallUrl: callURL, + }, + }, + }) + require.Error(t, errAlreadyExisting) + require.Nil(t, gotAlreadyExisting) + + gotRemove, errRemove := Tester.Client.System.RemoveQuota(SystemCTX, &system.RemoveQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + }) + require.NoError(t, errRemove) + require.Equal(t, gotRemove.Details.ResourceOwner, instanceID) + + gotRemoveAlready, errRemoveAlready := Tester.Client.System.RemoveQuota(SystemCTX, &system.RemoveQuotaRequest{ + InstanceId: instanceID, + Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, + }) + require.Error(t, errRemoveAlready) + require.Nil(t, gotRemoveAlready) +} diff --git a/internal/api/grpc/system/server_integration_test.go b/internal/api/grpc/system/server_integration_test.go new file mode 100644 index 000000000..f36972993 --- /dev/null +++ b/internal/api/grpc/system/server_integration_test.go @@ -0,0 +1,32 @@ +//go:build integration + +package system_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/zitadel/zitadel/internal/integration" +) + +var ( + CTX context.Context + SystemCTX context.Context + Tester *integration.Tester +) + +func TestMain(m *testing.M) { + os.Exit(func() int { + ctx, _, cancel := integration.Contexts(5 * time.Minute) + defer cancel() + CTX = ctx + + Tester = integration.NewTester(ctx) + defer Tester.Done() + + SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser) + return m.Run() + }()) +} diff --git a/internal/api/http/middleware/access_interceptor.go b/internal/api/http/middleware/access_interceptor.go index 7d05619ec..538ec226d 100644 --- a/internal/api/http/middleware/access_interceptor.go +++ b/internal/api/http/middleware/access_interceptor.go @@ -14,12 +14,12 @@ import ( "github.com/zitadel/zitadel/internal/api/grpc/server/middleware" http_utils "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/logstore/emitters/access" + "github.com/zitadel/zitadel/internal/logstore/record" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) type AccessInterceptor struct { - svc *logstore.Service + svc *logstore.Service[*record.AccessLog] cookieHandler *http_utils.CookieHandler limitConfig *AccessConfig storeOnly bool @@ -33,7 +33,7 @@ type AccessConfig struct { // NewAccessInterceptor intercepts all requests and stores them to the logstore. // If storeOnly is false, it also checks if requests are exhausted. // If requests are exhausted, it also returns http.StatusTooManyRequests and sets a cookie -func NewAccessInterceptor(svc *logstore.Service, cookieHandler *http_utils.CookieHandler, cookieConfig *AccessConfig) *AccessInterceptor { +func NewAccessInterceptor(svc *logstore.Service[*record.AccessLog], cookieHandler *http_utils.CookieHandler, cookieConfig *AccessConfig) *AccessInterceptor { return &AccessInterceptor{ svc: svc, cookieHandler: cookieHandler, @@ -50,7 +50,7 @@ func (a *AccessInterceptor) WithoutLimiting() *AccessInterceptor { } } -func (a *AccessInterceptor) AccessService() *logstore.Service { +func (a *AccessInterceptor) AccessService() *logstore.Service[*record.AccessLog] { return a.svc } @@ -81,46 +81,70 @@ func (a *AccessInterceptor) DeleteExhaustedCookie(writer http.ResponseWriter) { a.cookieHandler.DeleteCookie(writer, a.limitConfig.ExhaustedCookieKey) } +func (a *AccessInterceptor) HandleIgnorePathPrefixes(ignoredPathPrefixes []string) func(next http.Handler) http.Handler { + return a.handle(ignoredPathPrefixes...) +} + func (a *AccessInterceptor) Handle(next http.Handler) http.Handler { - if !a.svc.Enabled() { - return next - } - return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - ctx := request.Context() - tracingCtx, checkSpan := tracing.NewNamedSpan(ctx, "checkAccess") - wrappedWriter := &statusRecorder{ResponseWriter: writer, status: 0} - limited := a.Limit(tracingCtx) - checkSpan.End() - if limited { - a.SetExhaustedCookie(wrappedWriter, request) - http.Error(wrappedWriter, "quota for authenticated requests is exhausted", http.StatusTooManyRequests) + return a.handle()(next) +} + +func (a *AccessInterceptor) handle(ignoredPathPrefixes ...string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + if !a.svc.Enabled() { + return next } - if !limited && !a.storeOnly { - a.DeleteExhaustedCookie(wrappedWriter) - } - if !limited { - next.ServeHTTP(wrappedWriter, request) - } - tracingCtx, writeSpan := tracing.NewNamedSpan(tracingCtx, "writeAccess") - defer writeSpan.End() - requestURL := request.RequestURI - unescapedURL, err := url.QueryUnescape(requestURL) - if err != nil { - logging.WithError(err).WithField("url", requestURL).Warning("failed to unescape request url") - } - instance := authz.GetInstance(tracingCtx) - a.svc.Handle(tracingCtx, &access.Record{ - LogDate: time.Now(), - Protocol: access.HTTP, - RequestURL: unescapedURL, - ResponseStatus: uint32(wrappedWriter.status), - RequestHeaders: request.Header, - ResponseHeaders: writer.Header(), - InstanceID: instance.InstanceID(), - ProjectID: instance.ProjectID(), - RequestedDomain: instance.RequestedDomain(), - RequestedHost: instance.RequestedHost(), + return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + ctx := request.Context() + tracingCtx, checkSpan := tracing.NewNamedSpan(ctx, "checkAccessQuota") + wrappedWriter := &statusRecorder{ResponseWriter: writer, status: 0} + for _, ignoredPathPrefix := range ignoredPathPrefixes { + if !strings.HasPrefix(request.RequestURI, ignoredPathPrefix) { + continue + } + checkSpan.End() + next.ServeHTTP(wrappedWriter, request) + a.writeLog(tracingCtx, wrappedWriter, writer, request, true) + return + } + limited := a.Limit(tracingCtx) + checkSpan.End() + if limited { + a.SetExhaustedCookie(wrappedWriter, request) + http.Error(wrappedWriter, "quota for authenticated requests is exhausted", http.StatusTooManyRequests) + } + if !limited && !a.storeOnly { + a.DeleteExhaustedCookie(wrappedWriter) + } + if !limited { + next.ServeHTTP(wrappedWriter, request) + } + a.writeLog(tracingCtx, wrappedWriter, writer, request, a.storeOnly) }) + } +} + +func (a *AccessInterceptor) writeLog(ctx context.Context, wrappedWriter *statusRecorder, writer http.ResponseWriter, request *http.Request, notCountable bool) { + ctx, writeSpan := tracing.NewNamedSpan(ctx, "writeAccess") + defer writeSpan.End() + requestURL := request.RequestURI + unescapedURL, err := url.QueryUnescape(requestURL) + if err != nil { + logging.WithError(err).WithField("url", requestURL).Warning("failed to unescape request url") + } + instance := authz.GetInstance(ctx) + a.svc.Handle(ctx, &record.AccessLog{ + LogDate: time.Now(), + Protocol: record.HTTP, + RequestURL: unescapedURL, + ResponseStatus: uint32(wrappedWriter.status), + RequestHeaders: request.Header, + ResponseHeaders: writer.Header(), + InstanceID: instance.InstanceID(), + ProjectID: instance.ProjectID(), + RequestedDomain: instance.RequestedDomain(), + RequestedHost: instance.RequestedHost(), + NotCountable: notCountable, }) } diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index 62dad091e..ea630482c 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -7,6 +7,7 @@ import ( "time" "github.com/rakyll/statik/fs" + "github.com/zitadel/oidc/v2/pkg/oidc" "github.com/zitadel/oidc/v2/pkg/op" "golang.org/x/text/language" @@ -79,13 +80,32 @@ type OPStorage struct { assetAPIPrefix func(ctx context.Context) string } -func NewProvider(config Config, defaultLogoutRedirectURI string, externalSecure bool, command *command.Commands, query *query.Queries, repo repository.Repository, encryptionAlg crypto.EncryptionAlgorithm, cryptoKey []byte, es *eventstore.Eventstore, projections *database.DB, userAgentCookie, instanceHandler, accessHandler func(http.Handler) http.Handler) (op.OpenIDProvider, error) { +func NewProvider( + config Config, + defaultLogoutRedirectURI string, + externalSecure bool, + command *command.Commands, + query *query.Queries, + repo repository.Repository, + encryptionAlg crypto.EncryptionAlgorithm, + cryptoKey []byte, + es *eventstore.Eventstore, + projections *database.DB, + userAgentCookie, instanceHandler func(http.Handler) http.Handler, + accessHandler *middleware.AccessInterceptor, +) (op.OpenIDProvider, error) { opConfig, err := createOPConfig(config, defaultLogoutRedirectURI, cryptoKey) if err != nil { return nil, caos_errs.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w") } storage := newStorage(config, command, query, repo, encryptionAlg, es, projections, externalSecure) - options, err := createOptions(config, externalSecure, userAgentCookie, instanceHandler, accessHandler) + options, err := createOptions( + config, + externalSecure, + userAgentCookie, + instanceHandler, + accessHandler.HandleIgnorePathPrefixes(ignoredQuotaLimitEndpoint(config.CustomEndpoints)), + ) if err != nil { return nil, caos_errs.ThrowInternal(err, "OIDC-D3gq1", "cannot create options: %w") } @@ -101,6 +121,21 @@ func NewProvider(config Config, defaultLogoutRedirectURI string, externalSecure return provider, nil } +func ignoredQuotaLimitEndpoint(endpoints *EndpointConfig) []string { + authURL := op.DefaultEndpoints.Authorization.Relative() + keysURL := op.DefaultEndpoints.JwksURI.Relative() + if endpoints == nil { + return []string{oidc.DiscoveryEndpoint, authURL, keysURL} + } + if endpoints.Auth != nil && endpoints.Auth.Path != "" { + authURL = endpoints.Auth.Path + } + if endpoints.Keys != nil && endpoints.Keys.Path != "" { + keysURL = endpoints.Keys.Path + } + return []string{oidc.DiscoveryEndpoint, authURL, keysURL} +} + func createOPConfig(config Config, defaultLogoutRedirectURI string, cryptoKey []byte) (*op.Config, error) { supportedLanguages, err := getSupportedLanguages() if err != nil { diff --git a/internal/api/saml/provider.go b/internal/api/saml/provider.go index f46802f6a..644fe2886 100644 --- a/internal/api/saml/provider.go +++ b/internal/api/saml/provider.go @@ -38,8 +38,8 @@ func NewProvider( es *eventstore.Eventstore, projections *database.DB, instanceHandler, - userAgentCookie, - accessHandler func(http.Handler) http.Handler, + userAgentCookie func(http.Handler) http.Handler, + accessHandler *middleware.AccessInterceptor, ) (*provider.Provider, error) { metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount} @@ -63,7 +63,7 @@ func NewProvider( middleware.NoCacheInterceptor().Handler, instanceHandler, userAgentCookie, - accessHandler, + accessHandler.HandleIgnorePathPrefixes(ignoredQuotaLimitEndpoint(conf.ProviderConfig)), http_utils.CopyHeadersToContext, ), provider.WithCustomTimeFormat("2006-01-02T15:04:05.999Z"), @@ -100,3 +100,22 @@ func newStorage( defaultLoginURL: fmt.Sprintf("%s%s?%s=", login.HandlerPrefix, login.EndpointLogin, login.QueryAuthRequestID), }, nil } + +func ignoredQuotaLimitEndpoint(config *provider.Config) []string { + metadataEndpoint := HandlerPrefix + provider.DefaultMetadataEndpoint + certificateEndpoint := HandlerPrefix + provider.DefaultCertificateEndpoint + ssoEndpoint := HandlerPrefix + provider.DefaultSingleSignOnEndpoint + if config.MetadataConfig != nil && config.MetadataConfig.Path != "" { + metadataEndpoint = HandlerPrefix + config.MetadataConfig.Path + } + if config.IDPConfig == nil || config.IDPConfig.Endpoints == nil { + return []string{metadataEndpoint, certificateEndpoint, ssoEndpoint} + } + if config.IDPConfig.Endpoints.Certificate != nil && config.IDPConfig.Endpoints.Certificate.Relative() != "" { + certificateEndpoint = HandlerPrefix + config.IDPConfig.Endpoints.Certificate.Relative() + } + if config.IDPConfig.Endpoints.SingleSignOn != nil && config.IDPConfig.Endpoints.SingleSignOn.Relative() != "" { + ssoEndpoint = HandlerPrefix + config.IDPConfig.Endpoints.SingleSignOn.Relative() + } + return []string{metadataEndpoint, certificateEndpoint, ssoEndpoint} +} diff --git a/internal/command/instance.go b/internal/command/instance.go index e734988a5..18c13b6d4 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -283,10 +283,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str if err != nil { return "", "", nil, nil, err } - - quotaAggregate := quota.NewAggregate(quotaId, instanceID, instanceID) - - validations = append(validations, c.AddQuotaCommand(quotaAggregate, q)) + validations = append(validations, c.AddQuotaCommand(quota.NewAggregate(quotaId, instanceID), q)) } } diff --git a/internal/command/main_test.go b/internal/command/main_test.go index d1a2b3de5..2d798a40e 100644 --- a/internal/command/main_test.go +++ b/internal/command/main_test.go @@ -26,6 +26,7 @@ import ( "github.com/zitadel/zitadel/internal/repository/oidcsession" "github.com/zitadel/zitadel/internal/repository/org" proj_repo "github.com/zitadel/zitadel/internal/repository/project" + quota_repo "github.com/zitadel/zitadel/internal/repository/quota" "github.com/zitadel/zitadel/internal/repository/session" usr_repo "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/repository/usergrant" @@ -50,6 +51,7 @@ func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore { idpintent.RegisterEventMappers(es) authrequest.RegisterEventMappers(es) oidcsession.RegisterEventMappers(es) + quota_repo.RegisterEventMappers(es) return es } diff --git a/internal/command/quota.go b/internal/command/quota.go index 95e4d6fa8..b78792910 100644 --- a/internal/command/quota.go +++ b/internal/command/quota.go @@ -21,8 +21,8 @@ const ( QuotaActionsAllRunsSeconds QuotaUnit = "actions.all.runs.seconds" ) -func (q *QuotaUnit) Enum() quota.Unit { - switch *q { +func (q QuotaUnit) Enum() quota.Unit { + switch q { case QuotaRequestsAllAuthenticated: return quota.RequestsAllAuthenticated case QuotaActionsAllRunsSeconds: @@ -46,14 +46,11 @@ func (c *Commands) AddQuota( if wm.active { return nil, errors.ThrowAlreadyExists(nil, "COMMAND-WDfFf", "Errors.Quota.AlreadyExists") } - aggregateId, err := c.idGenerator.Next() if err != nil { return nil, err } - aggregate := quota.NewAggregate(aggregateId, instanceId, instanceId) - - cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.AddQuotaCommand(aggregate, q)) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.AddQuotaCommand(quota.NewAggregate(aggregateId, instanceId), q)) if err != nil { return nil, err } @@ -80,7 +77,7 @@ func (c *Commands) RemoveQuota(ctx context.Context, unit QuotaUnit) (*domain.Obj return nil, errors.ThrowNotFound(nil, "COMMAND-WDfFf", "Errors.Quota.NotFound") } - aggregate := quota.NewAggregate(wm.AggregateID, instanceId, instanceId) + aggregate := quota.NewAggregate(wm.AggregateID, instanceId) events := []eventstore.Command{ quota.NewRemovedEvent(ctx, &aggregate.Aggregate, unit.Enum()), @@ -109,6 +106,22 @@ type QuotaNotification struct { type QuotaNotifications []*QuotaNotification +func (q *QuotaNotification) validate() error { + u, err := url.Parse(q.CallURL) + if err != nil { + return errors.ThrowInvalidArgument(err, "QUOTA-bZ0Fj", "Errors.Quota.Invalid.CallURL") + } + + if !u.IsAbs() || u.Host == "" { + return errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "Errors.Quota.Invalid.CallURL") + } + + if q.Percent < 1 { + return errors.ThrowInvalidArgument(nil, "QUOTA-pBfjq", "Errors.Quota.Invalid.Percent") + } + return nil +} + func (q *QuotaNotifications) toAddedEventNotifications(idGenerator id.Generator) ([]*quota.AddedEventNotification, error) { if q == nil { return nil, nil @@ -144,17 +157,8 @@ type AddQuota struct { func (q *AddQuota) validate() error { for _, notification := range q.Notifications { - u, err := url.Parse(notification.CallURL) - if err != nil { - return errors.ThrowInvalidArgument(err, "QUOTA-bZ0Fj", "Errors.Quota.Invalid.CallURL") - } - - if !u.IsAbs() || u.Host == "" { - return errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "Errors.Quota.Invalid.CallURL") - } - - if notification.Percent < 1 { - return errors.ThrowInvalidArgument(nil, "QUOTA-pBfjq", "Errors.Quota.Invalid.Percent") + if err := notification.validate(); err != nil { + return err } } @@ -169,11 +173,6 @@ func (q *AddQuota) validate() error { if q.ResetInterval < time.Minute { return errors.ThrowInvalidArgument(nil, "QUOTA-R5otd", "Errors.Quota.Invalid.ResetInterval") } - - if !q.Limit && len(q.Notifications) == 0 { - return errors.ThrowInvalidArgument(nil, "QUOTA-4Nv68", "Errors.Quota.Invalid.Noop") - } - return nil } diff --git a/internal/command/quota_model.go b/internal/command/quota_model.go index 3da259893..6e05b4df0 100644 --- a/internal/command/quota_model.go +++ b/internal/command/quota_model.go @@ -41,6 +41,7 @@ func (wm *quotaWriteModel) Reduce() error { switch e := event.(type) { case *quota.AddedEvent: wm.AggregateID = e.Aggregate().ID + wm.ChangeDate = e.CreationDate() wm.active = true case *quota.RemovedEvent: wm.AggregateID = e.Aggregate().ID diff --git a/internal/command/quota_report.go b/internal/command/quota_report.go index 19855452e..b3fe9afd3 100644 --- a/internal/command/quota_report.go +++ b/internal/command/quota_report.go @@ -5,16 +5,47 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/telemetry/tracing" ) // ReportQuotaUsage writes a slice of *quota.NotificationDueEvent directly to the eventstore -func (c *Commands) ReportQuotaUsage(ctx context.Context, dueNotifications []*quota.NotificationDueEvent) error { - cmds := make([]eventstore.Command, len(dueNotifications)) - for idx, notification := range dueNotifications { - cmds[idx] = notification +func (c *Commands) ReportQuotaUsage(ctx context.Context, dueNotifications []*quota.NotificationDueEvent) (err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + cmds := make([]eventstore.Command, 0, len(dueNotifications)) + for _, notification := range dueNotifications { + ctxFilter, spanFilter := tracing.NewNamedSpan(ctx, "filterNotificationDueEvents") + events, errFilter := c.eventstore.Filter( + ctxFilter, + eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + InstanceID(notification.Aggregate().InstanceID). + AddQuery(). + AggregateTypes(quota.AggregateType). + AggregateIDs(notification.Aggregate().ID). + EventTypes(quota.NotificationDueEventType). + EventData(map[string]interface{}{ + "id": notification.ID, + "periodStart": notification.PeriodStart, + "threshold": notification.Threshold, + }).Builder(), + ) + spanFilter.EndWithError(errFilter) + if errFilter != nil { + return errFilter + } + if len(events) > 0 { + continue + } + cmds = append(cmds, notification) } - _, err := c.eventstore.Push(ctx, cmds...) - return err + if len(cmds) == 0 { + return nil + } + ctxPush, spanPush := tracing.NewNamedSpan(ctx, "pushNotificationDueEvents") + _, errPush := c.eventstore.Push(ctxPush, cmds...) + spanPush.EndWithError(errPush) + return errPush } func (c *Commands) UsageNotificationSent(ctx context.Context, dueEvent *quota.NotificationDueEvent) error { diff --git a/internal/command/quota_report_test.go b/internal/command/quota_report_test.go new file mode 100644 index 000000000..214b67ac7 --- /dev/null +++ b/internal/command/quota_report_test.go @@ -0,0 +1,307 @@ +package command + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/id" + id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +func TestQuotaReport_ReportQuotaUsage(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + } + type args struct { + ctx context.Context + dueNotifications []*quota.NotificationDueEvent + } + type res struct { + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "no due events", + fields: fields{ + eventstore: eventstoreExpect( + t, + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + }, + res: res{}, + }, + { + name: "due event already reported", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 200, + ), + ), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + dueNotifications: []*quota.NotificationDueEvent{ + { + Unit: QuotaRequestsAllAuthenticated.Enum(), + ID: "id", + CallURL: "url", + PeriodStart: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + Threshold: 1000, + Usage: 250, + }, + }, + }, + res: res{}, + }, + { + name: "due event not reported", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + ), + }, + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + dueNotifications: []*quota.NotificationDueEvent{ + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + }, + }, + res: res{}, + }, + { + name: "due events", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id2", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + ), + ), + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + ), + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id3", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + ), + }, + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + dueNotifications: []*quota.NotificationDueEvent{ + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id2", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id3", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + }, + }, + res: res{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore, + } + err := r.ReportQuotaUsage(tt.args.ctx, tt.args.dueNotifications) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestQuotaReport_UsageNotificationSent(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + ctx context.Context + dueNotification *quota.NotificationDueEvent + } + type res struct { + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "usage notification sent, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewNotifiedEvent( + context.Background(), + "quota1", + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + ), + ), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + dueNotification: quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + }, + res: res{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + err := r.UsageNotificationSent(tt.args.ctx, tt.args.dueNotification) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} diff --git a/internal/command/quota_test.go b/internal/command/quota_test.go new file mode 100644 index 000000000..a3f42b591 --- /dev/null +++ b/internal/command/quota_test.go @@ -0,0 +1,638 @@ +package command + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/domain" + caos_errors "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/id" + id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +func TestQuota_AddQuota(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + ctx context.Context + addQuota *AddQuota + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "already existing", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Now(), + 30*24*time.Hour, + 1000, + false, + nil, + ), + ), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Time{}, + ResetInterval: 0, + Amount: 0, + Limit: false, + Notifications: nil, + }, + }, + res: res{ + err: caos_errors.IsErrorAlreadyExists, + }, + }, + { + name: "create quota, validation fail", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + addQuota: &AddQuota{ + Unit: "unimplemented", + From: time.Time{}, + ResetInterval: 0, + Amount: 0, + Limit: false, + Notifications: nil, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", "")) + }, + }, + }, + { + name: "create quota, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 30*24*time.Hour, + 1000, + true, + nil, + ), + ), + }, + uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + { + name: "removed, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Now(), + 30*24*time.Hour, + 1000, + true, + nil, + ), + ), + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewRemovedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 30*24*time.Hour, + 1000, + true, + nil, + ), + ), + }, + uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + { + name: "create quota with notifications, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 30*24*time.Hour, + 1000, + true, + []*quota.AddedEventNotification{ + { + ID: "notification1", + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }, + }, + ), + ), + }, + uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: QuotaNotifications{ + { + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := r.AddQuota(tt.args.ctx, tt.args.addQuota) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestQuota_RemoveQuota(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + } + type args struct { + ctx context.Context + unit QuotaUnit + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "not found", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + unit: QuotaRequestsAllAuthenticated, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowNotFound(nil, "COMMAND-WDfFf", "")) + }, + }, + }, + { + name: "already removed", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Now(), + 30*24*time.Hour, + 1000, + true, + nil, + ), + ), + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewRemovedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + ), + ), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + unit: QuotaRequestsAllAuthenticated, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowNotFound(nil, "COMMAND-WDfFf", "")) + }, + }, + }, + { + name: "remove quota, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewAddedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + time.Now(), + 30*24*time.Hour, + 1000, + false, + nil, + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewRemovedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + ), + ), + }, + uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewRemoveQuotaNameUniqueConstraint(quota.RequestsAllAuthenticated)), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + unit: QuotaRequestsAllAuthenticated, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore, + } + got, err := r.RemoveQuota(tt.args.ctx, tt.args.unit) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestQuota_QuotaNotification_validate(t *testing.T) { + type args struct { + quotaNotification *QuotaNotification + } + type res struct { + err func(error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "notification url parse failed", + args: args{ + quotaNotification: &QuotaNotification{ + Percent: 20, + Repeat: false, + CallURL: "%", + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-bZ0Fj", "")) + }, + }, + }, + { + name: "notification url parse empty schema", + args: args{ + quotaNotification: &QuotaNotification{ + Percent: 20, + Repeat: false, + CallURL: "localhost:8080", + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "")) + }, + }, + }, + { + name: "notification url parse empty host", + args: args{ + quotaNotification: &QuotaNotification{ + Percent: 20, + Repeat: false, + CallURL: "https://", + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "")) + }, + }, + }, + { + name: "notification url parse percent 0", + args: args{ + quotaNotification: &QuotaNotification{ + Percent: 0, + Repeat: false, + CallURL: "https://localhost:8080", + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-pBfjq", "")) + }, + }, + }, + { + name: "notification, ok", + args: args{ + quotaNotification: &QuotaNotification{ + Percent: 20, + Repeat: false, + CallURL: "https://localhost:8080", + }, + }, + res: res{ + err: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.quotaNotification.validate() + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestQuota_AddQuota_validate(t *testing.T) { + type args struct { + addQuota *AddQuota + } + type res struct { + err func(error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "notification url parse failed", + args: args{ + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Now(), + ResetInterval: time.Minute * 10, + Amount: 100, + Limit: true, + Notifications: QuotaNotifications{ + { + Percent: 20, + Repeat: false, + CallURL: "%", + }, + }, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-bZ0Fj", "")) + }, + }, + }, + { + name: "unit unimplemented", + args: args{ + addQuota: &AddQuota{ + Unit: "unimplemented", + From: time.Now(), + ResetInterval: time.Minute * 10, + Amount: 100, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", "")) + }, + }, + }, + { + name: "amount 0", + args: args{ + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Now(), + ResetInterval: time.Minute * 10, + Amount: 0, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-hOKSJ", "")) + }, + }, + }, + { + name: "reset interval under 1 min", + args: args{ + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Now(), + ResetInterval: time.Second * 10, + Amount: 100, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-R5otd", "")) + }, + }, + }, + { + name: "validate, ok", + args: args{ + addQuota: &AddQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Now(), + ResetInterval: time.Minute * 10, + Amount: 100, + Limit: false, + Notifications: nil, + }, + }, + res: res{ + err: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.addQuota.validate() + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} diff --git a/internal/database/type.go b/internal/database/type.go index 8360bc1f1..4b863c101 100644 --- a/internal/database/type.go +++ b/internal/database/type.go @@ -3,6 +3,7 @@ package database import ( "database/sql/driver" "encoding/json" + "time" "github.com/jackc/pgtype" ) @@ -93,3 +94,15 @@ func (m Map[V]) Value() (driver.Value, error) { } return json.Marshal(m) } + +type Duration time.Duration + +// Scan implements the [database/sql.Scanner] interface. +func (d *Duration) Scan(src any) error { + interval := new(pgtype.Interval) + if err := interval.Scan(src); err != nil { + return err + } + *d = Duration(time.Duration(interval.Microseconds*1000) + time.Duration(interval.Days)*24*time.Hour + time.Duration(interval.Months)*30*24*time.Hour) + return nil +} diff --git a/internal/integration/assert.go b/internal/integration/assert.go index 5c19eea98..3638c831a 100644 --- a/internal/integration/assert.go +++ b/internal/integration/assert.go @@ -24,8 +24,8 @@ type DetailsMsg interface { // // The resource owner is compared with expected and is // therefore the only value that has to be set. -func AssertDetails[D DetailsMsg](t testing.TB, exptected, actual D) { - wantDetails, gotDetails := exptected.GetDetails(), actual.GetDetails() +func AssertDetails[D DetailsMsg](t testing.TB, expected, actual D) { + wantDetails, gotDetails := expected.GetDetails(), actual.GetDetails() if wantDetails == nil { assert.Nil(t, gotDetails) return diff --git a/internal/integration/config/zitadel.yaml b/internal/integration/config/zitadel.yaml index 14222e263..22d77a033 100644 --- a/internal/integration/config/zitadel.yaml +++ b/internal/integration/config/zitadel.yaml @@ -22,22 +22,17 @@ FirstInstance: PasswordChangeRequired: false LogStore: - Access: - Database: - Enabled: true - Debounce: - MinFrequency: 0s - MaxBulkSize: 0 Execution: - Database: - Enabled: true Stdout: Enabled: true Quotas: Access: + Enabled: true ExhaustedCookieKey: "zitadel.quota.limiting" ExhaustedCookieMaxAge: "60s" + Execution: + Enabled: true Projections: Customizations: diff --git a/internal/integration/integration.go b/internal/integration/integration.go index d65231119..3e270b1d7 100644 --- a/internal/integration/integration.go +++ b/internal/integration/integration.go @@ -8,7 +8,11 @@ import ( _ "embed" "errors" "fmt" + "io" + "net/http" + "net/http/httptest" "os" + "reflect" "strings" "sync" "time" @@ -30,6 +34,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/net" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/webauthn" "github.com/zitadel/zitadel/pkg/grpc/admin" @@ -71,6 +76,11 @@ const ( UserPassword = "VeryS3cret!" ) +const ( + PortMilestoneServer = "8081" + PortQuotaServer = "8082" +) + // User information with a Personal Access Token. type User struct { *query.User @@ -101,6 +111,11 @@ type Tester struct { Organisation *query.Org Users InstanceUserMap + MilestoneChan chan []byte + milestoneServer *httptest.Server + QuotaNotificationChan chan []byte + quotaNotificationServer *httptest.Server + Client Client WebAuthN *webauthn.Client wg sync.WaitGroup // used for shutdown @@ -271,6 +286,8 @@ func (s *Tester) Done() { s.Shutdown <- os.Interrupt s.wg.Wait() + s.milestoneServer.Close() + s.quotaNotificationServer.Close() } // NewTester start a new Zitadel server by passing the default commandline. @@ -279,13 +296,13 @@ func (s *Tester) Done() { // INTEGRATION_DB_FLAVOR environment variable and can have the values "cockroach" // or "postgres". Defaults to "cockroach". // -// The deault Instance and Organisation are read from the DB and system +// The default Instance and Organisation are read from the DB and system // users are created as needed. // // After the server is started, a [grpc.ClientConn] will be created and // the server is polled for it's health status. // -// Note: the database must already be setup and intialized before +// Note: the database must already be setup and initialized before // using NewTester. See the CONTRIBUTING.md document for details. func NewTester(ctx context.Context) *Tester { args := strings.Split(commandLine, " ") @@ -311,6 +328,13 @@ func NewTester(ctx context.Context) *Tester { tester := Tester{ Users: make(InstanceUserMap), } + tester.MilestoneChan = make(chan []byte, 100) + tester.milestoneServer, err = runMilestoneServer(ctx, tester.MilestoneChan) + logging.OnError(err).Fatal() + tester.QuotaNotificationChan = make(chan []byte, 100) + tester.quotaNotificationServer, err = runQuotaServer(ctx, tester.QuotaNotificationChan) + logging.OnError(err).Fatal() + tester.wg.Add(1) go func(wg *sync.WaitGroup) { logging.OnError(cmd.Execute()).Fatal() @@ -328,7 +352,6 @@ func NewTester(ctx context.Context) *Tester { tester.createMachineUserOrgOwner(ctx) tester.createMachineUserInstanceOwner(ctx) tester.WebAuthN = webauthn.NewClient(tester.Config.WebAuthNName, tester.Config.ExternalDomain, "https://"+tester.Host()) - return &tester } @@ -338,3 +361,51 @@ func Contexts(timeout time.Duration) (ctx, errCtx context.Context, cancel contex ctx, cancel = context.WithTimeout(context.Background(), timeout) return ctx, errCtx, cancel } + +func runMilestoneServer(ctx context.Context, bodies chan []byte) (*httptest.Server, error) { + mockServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if r.Header.Get("single-value") != "single-value" { + http.Error(w, "single-value header not set", http.StatusInternalServerError) + return + } + if reflect.DeepEqual(r.Header.Get("multi-value"), "multi-value-1,multi-value-2") { + http.Error(w, "single-value header not set", http.StatusInternalServerError) + return + } + bodies <- body + w.WriteHeader(http.StatusOK) + })) + config := net.ListenConfig() + listener, err := config.Listen(ctx, "tcp", ":"+PortMilestoneServer) + if err != nil { + return nil, err + } + mockServer.Listener = listener + mockServer.Start() + return mockServer, nil +} + +func runQuotaServer(ctx context.Context, bodies chan []byte) (*httptest.Server, error) { + mockServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + bodies <- body + w.WriteHeader(http.StatusOK) + })) + config := net.ListenConfig() + listener, err := config.Listen(ctx, "tcp", ":"+PortQuotaServer) + if err != nil { + return nil, err + } + mockServer.Listener = listener + mockServer.Start() + return mockServer, nil +} diff --git a/internal/logstore/config.go b/internal/logstore/config.go index 3e8a8d327..4f1419d53 100644 --- a/internal/logstore/config.go +++ b/internal/logstore/config.go @@ -6,6 +6,9 @@ type Configs struct { } type Config struct { - Database *EmitterConfig - Stdout *EmitterConfig + Stdout *StdConfig +} + +type StdConfig struct { + Enabled bool } diff --git a/internal/logstore/debouncer.go b/internal/logstore/debouncer.go index e299c08a8..9c8fefb0d 100644 --- a/internal/logstore/debouncer.go +++ b/internal/logstore/debouncer.go @@ -9,19 +9,17 @@ import ( "github.com/zitadel/logging" ) -type bulkSink interface { - sendBulk(ctx context.Context, bulk []LogRecord) error +type bulkSink[T LogRecord[T]] interface { + SendBulk(ctx context.Context, bulk []T) error } -var _ bulkSink = bulkSinkFunc(nil) +type bulkSinkFunc[T LogRecord[T]] func(ctx context.Context, bulk []T) error -type bulkSinkFunc func(ctx context.Context, items []LogRecord) error - -func (s bulkSinkFunc) sendBulk(ctx context.Context, items []LogRecord) error { - return s(ctx, items) +func (s bulkSinkFunc[T]) SendBulk(ctx context.Context, bulk []T) error { + return s(ctx, bulk) } -type debouncer struct { +type debouncer[T LogRecord[T]] struct { // Storing context.Context in a struct is generally bad practice // https://go.dev/blog/context-and-structs // However, debouncer starts a go routine that triggers side effects itself. @@ -33,8 +31,8 @@ type debouncer struct { ticker *clock.Ticker mux sync.Mutex cfg DebouncerConfig - storage bulkSink - cache []LogRecord + storage bulkSink[T] + cache []T cacheLen uint } @@ -43,8 +41,8 @@ type DebouncerConfig struct { MaxBulkSize uint } -func newDebouncer(binarySignaledCtx context.Context, cfg DebouncerConfig, clock clock.Clock, ship bulkSink) *debouncer { - a := &debouncer{ +func newDebouncer[T LogRecord[T]](binarySignaledCtx context.Context, cfg DebouncerConfig, clock clock.Clock, ship bulkSink[T]) *debouncer[T] { + a := &debouncer[T]{ binarySignaledCtx: binarySignaledCtx, clock: clock, cfg: cfg, @@ -58,7 +56,7 @@ func newDebouncer(binarySignaledCtx context.Context, cfg DebouncerConfig, clock return a } -func (d *debouncer) add(item LogRecord) { +func (d *debouncer[T]) add(item T) { d.mux.Lock() defer d.mux.Unlock() d.cache = append(d.cache, item) @@ -69,13 +67,13 @@ func (d *debouncer) add(item LogRecord) { } } -func (d *debouncer) ship() { +func (d *debouncer[T]) ship() { if d.cacheLen == 0 { return } d.mux.Lock() defer d.mux.Unlock() - if err := d.storage.sendBulk(d.binarySignaledCtx, d.cache); err != nil { + if err := d.storage.SendBulk(d.binarySignaledCtx, d.cache); err != nil { logging.WithError(err).WithField("size", len(d.cache)).Error("storing bulk failed") } d.cache = nil @@ -85,7 +83,7 @@ func (d *debouncer) ship() { } } -func (d *debouncer) shipOnTicks() { +func (d *debouncer[T]) shipOnTicks() { for range d.ticker.C { d.ship() } diff --git a/internal/logstore/emitter.go b/internal/logstore/emitter.go index a7a5151af..f35efc61a 100644 --- a/internal/logstore/emitter.go +++ b/internal/logstore/emitter.go @@ -2,56 +2,52 @@ package logstore import ( "context" - "fmt" "time" "github.com/benbjohnson/clock" - "github.com/zitadel/logging" ) type EmitterConfig struct { - Enabled bool - Keep time.Duration - CleanupInterval time.Duration - Debounce *DebouncerConfig + Enabled bool + Debounce *DebouncerConfig } -type emitter struct { +type emitter[T LogRecord[T]] struct { enabled bool ctx context.Context - debouncer *debouncer - emitter LogEmitter + debouncer *debouncer[T] + emitter LogEmitter[T] clock clock.Clock } -type LogRecord interface { - Normalize() LogRecord +type LogRecord[T any] interface { + Normalize() T } -type LogRecordFunc func() LogRecord +type LogRecordFunc[T any] func() T -func (r LogRecordFunc) Normalize() LogRecord { +func (r LogRecordFunc[T]) Normalize() T { return r() } -type LogEmitter interface { - Emit(ctx context.Context, bulk []LogRecord) error +type LogEmitter[T LogRecord[T]] interface { + Emit(ctx context.Context, bulk []T) error } -type LogEmitterFunc func(ctx context.Context, bulk []LogRecord) error +type LogEmitterFunc[T LogRecord[T]] func(ctx context.Context, bulk []T) error -func (l LogEmitterFunc) Emit(ctx context.Context, bulk []LogRecord) error { +func (l LogEmitterFunc[T]) Emit(ctx context.Context, bulk []T) error { return l(ctx, bulk) } -type LogCleanupper interface { - LogEmitter +type LogCleanupper[T LogRecord[T]] interface { Cleanup(ctx context.Context, keep time.Duration) error + LogEmitter[T] } // NewEmitter accepts Clock from github.com/benbjohnson/clock so we can control timers and tickers in the unit tests -func NewEmitter(ctx context.Context, clock clock.Clock, cfg *EmitterConfig, logger LogEmitter) (*emitter, error) { - svc := &emitter{ +func NewEmitter[T LogRecord[T]](ctx context.Context, clock clock.Clock, cfg *EmitterConfig, logger LogEmitter[T]) (*emitter[T], error) { + svc := &emitter[T]{ enabled: cfg != nil && cfg.Enabled, ctx: ctx, emitter: logger, @@ -63,36 +59,12 @@ func NewEmitter(ctx context.Context, clock clock.Clock, cfg *EmitterConfig, logg } if cfg.Debounce != nil && (cfg.Debounce.MinFrequency > 0 || cfg.Debounce.MaxBulkSize > 0) { - svc.debouncer = newDebouncer(ctx, *cfg.Debounce, clock, newStorageBulkSink(svc.emitter)) - } - - cleanupper, ok := logger.(LogCleanupper) - if !ok { - if cfg.Keep != 0 { - return nil, fmt.Errorf("cleaning up for this storage type is not supported, so keep duration must be 0, but is %d", cfg.Keep) - } - if cfg.CleanupInterval != 0 { - return nil, fmt.Errorf("cleaning up for this storage type is not supported, so cleanup interval duration must be 0, but is %d", cfg.Keep) - } - - return svc, nil - } - - if cfg.Keep != 0 && cfg.CleanupInterval != 0 { - go svc.startCleanupping(cleanupper, cfg.CleanupInterval, cfg.Keep) + svc.debouncer = newDebouncer[T](ctx, *cfg.Debounce, clock, newStorageBulkSink(svc.emitter)) } return svc, nil } -func (s *emitter) startCleanupping(cleanupper LogCleanupper, cleanupInterval, keep time.Duration) { - for range s.clock.Tick(cleanupInterval) { - if err := cleanupper.Cleanup(s.ctx, keep); err != nil { - logging.WithError(err).Error("cleaning up logs failed") - } - } -} - -func (s *emitter) Emit(ctx context.Context, record LogRecord) (err error) { +func (s *emitter[T]) Emit(ctx context.Context, record T) (err error) { if !s.enabled { return nil } @@ -102,11 +74,11 @@ func (s *emitter) Emit(ctx context.Context, record LogRecord) (err error) { return nil } - return s.emitter.Emit(ctx, []LogRecord{record}) + return s.emitter.Emit(ctx, []T{record}) } -func newStorageBulkSink(emitter LogEmitter) bulkSinkFunc { - return func(ctx context.Context, bulk []LogRecord) error { +func newStorageBulkSink[T LogRecord[T]](emitter LogEmitter[T]) bulkSinkFunc[T] { + return func(ctx context.Context, bulk []T) error { return emitter.Emit(ctx, bulk) } } diff --git a/internal/logstore/emitters/access/database.go b/internal/logstore/emitters/access/database.go index de3afe5ae..dc60f4922 100644 --- a/internal/logstore/emitters/access/database.go +++ b/internal/logstore/emitters/access/database.go @@ -3,167 +3,91 @@ package access import ( "context" "database/sql" - "fmt" - "net/http" - "strings" + "errors" "time" - "github.com/Masterminds/squirrel" - "github.com/zitadel/logging" - "google.golang.org/grpc/codes" - - "github.com/zitadel/zitadel/internal/api/call" - zitadel_http "github.com/zitadel/zitadel/internal/api/http" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/database" - caos_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -const ( - accessLogsTable = "logstore.access" - accessTimestampCol = "log_date" - accessProtocolCol = "protocol" - accessRequestURLCol = "request_url" - accessResponseStatusCol = "response_status" - accessRequestHeadersCol = "request_headers" - accessResponseHeadersCol = "response_headers" - accessInstanceIdCol = "instance_id" - accessProjectIdCol = "project_id" - accessRequestedDomainCol = "requested_domain" - accessRequestedHostCol = "requested_host" -) - -var _ logstore.UsageQuerier = (*databaseLogStorage)(nil) -var _ logstore.LogCleanupper = (*databaseLogStorage)(nil) +var _ logstore.UsageStorer[*record.AccessLog] = (*databaseLogStorage)(nil) type databaseLogStorage struct { dbClient *database.DB + commands *command.Commands + queries *query.Queries } -func NewDatabaseLogStorage(dbClient *database.DB) *databaseLogStorage { - return &databaseLogStorage{dbClient: dbClient} +func NewDatabaseLogStorage(dbClient *database.DB, commands *command.Commands, queries *query.Queries) *databaseLogStorage { + return &databaseLogStorage{dbClient: dbClient, commands: commands, queries: queries} } func (l *databaseLogStorage) QuotaUnit() quota.Unit { return quota.RequestsAllAuthenticated } -func (l *databaseLogStorage) Emit(ctx context.Context, bulk []logstore.LogRecord) error { +func (l *databaseLogStorage) Emit(ctx context.Context, bulk []*record.AccessLog) error { if len(bulk) == 0 { return nil } - builder := squirrel.Insert(accessLogsTable). - Columns( - accessTimestampCol, - accessProtocolCol, - accessRequestURLCol, - accessResponseStatusCol, - accessRequestHeadersCol, - accessResponseHeadersCol, - accessInstanceIdCol, - accessProjectIdCol, - accessRequestedDomainCol, - accessRequestedHostCol, - ). - PlaceholderFormat(squirrel.Dollar) - - for idx := range bulk { - item := bulk[idx].(*Record) - builder = builder.Values( - item.LogDate, - item.Protocol, - item.RequestURL, - item.ResponseStatus, - item.RequestHeaders, - item.ResponseHeaders, - item.InstanceID, - item.ProjectID, - item.RequestedDomain, - item.RequestedHost, - ) - } - - stmt, args, err := builder.ToSql() - if err != nil { - return caos_errors.ThrowInternal(err, "ACCESS-KOS7I", "Errors.Internal") - } - - result, err := l.dbClient.ExecContext(ctx, stmt, args...) - if err != nil { - return caos_errors.ThrowInternal(err, "ACCESS-alnT9", "Errors.Access.StorageFailed") - } - - rows, err := result.RowsAffected() - if err != nil { - return caos_errors.ThrowInternal(err, "ACCESS-7KIpL", "Errors.Internal") - } - - logging.WithFields("rows", rows).Debug("successfully stored access logs") - return nil + return l.incrementUsage(ctx, bulk) } -func (l *databaseLogStorage) QueryUsage(ctx context.Context, instanceId string, start time.Time) (uint64, error) { - stmt, args, err := squirrel.Select( - fmt.Sprintf("count(%s)", accessInstanceIdCol), - ). - From(accessLogsTable + l.dbClient.Timetravel(call.Took(ctx))). - Where(squirrel.And{ - squirrel.Eq{accessInstanceIdCol: instanceId}, - squirrel.GtOrEq{accessTimestampCol: start}, - squirrel.Expr(fmt.Sprintf(`%s #>> '{%s,0}' = '[REDACTED]'`, accessRequestHeadersCol, strings.ToLower(zitadel_http.Authorization))), - squirrel.NotLike{accessRequestURLCol: "%/zitadel.system.v1.SystemService/%"}, - squirrel.NotLike{accessRequestURLCol: "%/system/v1/%"}, - squirrel.Or{ - squirrel.And{ - squirrel.Eq{accessProtocolCol: HTTP}, - squirrel.NotEq{accessResponseStatusCol: http.StatusForbidden}, - squirrel.NotEq{accessResponseStatusCol: http.StatusInternalServerError}, - squirrel.NotEq{accessResponseStatusCol: http.StatusTooManyRequests}, - }, - squirrel.And{ - squirrel.Eq{accessProtocolCol: GRPC}, - squirrel.NotEq{accessResponseStatusCol: codes.PermissionDenied}, - squirrel.NotEq{accessResponseStatusCol: codes.Internal}, - squirrel.NotEq{accessResponseStatusCol: codes.ResourceExhausted}, - }, - }, - }). - PlaceholderFormat(squirrel.Dollar). - ToSql() +func (l *databaseLogStorage) incrementUsage(ctx context.Context, bulk []*record.AccessLog) (err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() - if err != nil { - return 0, caos_errors.ThrowInternal(err, "ACCESS-V9Sde", "Errors.Internal") + byInstance := make(map[string][]*record.AccessLog) + for _, r := range bulk { + if r.InstanceID != "" { + byInstance[r.InstanceID] = append(byInstance[r.InstanceID], r) + } } - - var count uint64 - err = l.dbClient. - QueryRowContext(ctx, - func(row *sql.Row) error { - return row.Scan(&count) - }, - stmt, args..., - ) - - if err != nil { - return 0, caos_errors.ThrowInternal(err, "ACCESS-pBPrM", "Errors.Logstore.Access.ScanFailed") + for instanceID, instanceBulk := range byInstance { + q, getQuotaErr := l.queries.GetQuota(ctx, instanceID, quota.RequestsAllAuthenticated) + if errors.Is(getQuotaErr, sql.ErrNoRows) { + continue + } + err = errors.Join(err, getQuotaErr) + if getQuotaErr != nil { + continue + } + sum, incrementErr := l.incrementUsageFromAccessLogs(ctx, instanceID, q.CurrentPeriodStart, instanceBulk) + err = errors.Join(err, incrementErr) + if incrementErr != nil { + continue + } + notifications, getNotificationErr := l.queries.GetDueQuotaNotifications(ctx, instanceID, quota.RequestsAllAuthenticated, q, q.CurrentPeriodStart, sum) + err = errors.Join(err, getNotificationErr) + if getNotificationErr != nil || len(notifications) == 0 { + continue + } + ctx = authz.WithInstanceID(ctx, instanceID) + reportErr := l.commands.ReportQuotaUsage(ctx, notifications) + err = errors.Join(err, reportErr) + if reportErr != nil { + continue + } } - - return count, nil -} - -func (l *databaseLogStorage) Cleanup(ctx context.Context, keep time.Duration) error { - stmt, args, err := squirrel.Delete(accessLogsTable). - Where(squirrel.LtOrEq{accessTimestampCol: time.Now().Add(-keep)}). - PlaceholderFormat(squirrel.Dollar). - ToSql() - - if err != nil { - return caos_errors.ThrowInternal(err, "ACCESS-2oTh6", "Errors.Internal") - } - - execCtx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - _, err = l.dbClient.ExecContext(execCtx, stmt, args...) return err } + +func (l *databaseLogStorage) incrementUsageFromAccessLogs(ctx context.Context, instanceID string, periodStart time.Time, records []*record.AccessLog) (sum uint64, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + var count uint64 + for _, r := range records { + if r.IsAuthenticated() { + count++ + } + } + return projection.QuotaProjection.IncrementUsage(ctx, quota.RequestsAllAuthenticated, instanceID, periodStart, count) +} diff --git a/internal/logstore/emitters/access/record.go b/internal/logstore/emitters/access/record.go deleted file mode 100644 index 6b802015d..000000000 --- a/internal/logstore/emitters/access/record.go +++ /dev/null @@ -1,97 +0,0 @@ -package access - -import ( - "strings" - "time" - - zitadel_http "github.com/zitadel/zitadel/internal/api/http" - "github.com/zitadel/zitadel/internal/logstore" -) - -var _ logstore.LogRecord = (*Record)(nil) - -type Record struct { - LogDate time.Time `json:"logDate"` - Protocol Protocol `json:"protocol"` - RequestURL string `json:"requestUrl"` - ResponseStatus uint32 `json:"responseStatus"` - // RequestHeaders are plain maps so varying implementation - // between HTTP and gRPC don't interfere with each other - RequestHeaders map[string][]string `json:"requestHeaders"` - // ResponseHeaders are plain maps so varying implementation - // between HTTP and gRPC don't interfere with each other - ResponseHeaders map[string][]string `json:"responseHeaders"` - InstanceID string `json:"instanceId"` - ProjectID string `json:"projectId"` - RequestedDomain string `json:"requestedDomain"` - RequestedHost string `json:"requestedHost"` -} - -type Protocol uint8 - -const ( - GRPC Protocol = iota - HTTP - - redacted = "[REDACTED]" -) - -func (a Record) Normalize() logstore.LogRecord { - a.RequestedDomain = cutString(a.RequestedDomain, 200) - a.RequestURL = cutString(a.RequestURL, 200) - a.RequestHeaders = normalizeHeaders(a.RequestHeaders, strings.ToLower(zitadel_http.Authorization), "grpcgateway-authorization", "cookie", "grpcgateway-cookie") - a.ResponseHeaders = normalizeHeaders(a.ResponseHeaders, "set-cookie") - return &a -} - -// normalizeHeaders lowers all header keys and redacts secrets -func normalizeHeaders(header map[string][]string, redactKeysLower ...string) map[string][]string { - return pruneKeys(redactKeys(lowerKeys(header), redactKeysLower...)) -} - -func lowerKeys(header map[string][]string) map[string][]string { - lower := make(map[string][]string, len(header)) - for k, v := range header { - lower[strings.ToLower(k)] = v - } - return lower -} - -func redactKeys(header map[string][]string, redactKeysLower ...string) map[string][]string { - redactedKeys := make(map[string][]string, len(header)) - for k, v := range header { - redactedKeys[k] = v - } - for _, redactKey := range redactKeysLower { - if _, ok := redactedKeys[redactKey]; ok { - redactedKeys[redactKey] = []string{redacted} - } - } - return redactedKeys -} - -const maxValuesPerKey = 10 - -func pruneKeys(header map[string][]string) map[string][]string { - prunedKeys := make(map[string][]string, len(header)) - for key, value := range header { - valueItems := make([]string, 0, maxValuesPerKey) - for i, valueItem := range value { - // Max 10 header values per key - if i > maxValuesPerKey { - break - } - // Max 200 value length - valueItems = append(valueItems, cutString(valueItem, 200)) - } - prunedKeys[key] = valueItems - } - return prunedKeys -} - -func cutString(str string, pos int) string { - if len(str) <= pos { - return str - } - return str[:pos-1] -} diff --git a/internal/logstore/emitters/execution/database.go b/internal/logstore/emitters/execution/database.go index 396f3f2ea..8f7985de1 100644 --- a/internal/logstore/emitters/execution/database.go +++ b/internal/logstore/emitters/execution/database.go @@ -3,142 +3,84 @@ package execution import ( "context" "database/sql" - "fmt" + "errors" + "math" "time" - "github.com/Masterminds/squirrel" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/api/call" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/database" - caos_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/logstore/record" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/quota" ) -const ( - executionLogsTable = "logstore.execution" - executionTimestampCol = "log_date" - executionTookCol = "took" - executionMessageCol = "message" - executionLogLevelCol = "loglevel" - executionInstanceIdCol = "instance_id" - executionActionIdCol = "action_id" - executionMetadataCol = "metadata" -) - -var _ logstore.UsageQuerier = (*databaseLogStorage)(nil) -var _ logstore.LogCleanupper = (*databaseLogStorage)(nil) +var _ logstore.UsageStorer[*record.ExecutionLog] = (*databaseLogStorage)(nil) type databaseLogStorage struct { dbClient *database.DB + commands *command.Commands + queries *query.Queries } -func NewDatabaseLogStorage(dbClient *database.DB) *databaseLogStorage { - return &databaseLogStorage{dbClient: dbClient} +func NewDatabaseLogStorage(dbClient *database.DB, commands *command.Commands, queries *query.Queries) *databaseLogStorage { + return &databaseLogStorage{dbClient: dbClient, commands: commands, queries: queries} } func (l *databaseLogStorage) QuotaUnit() quota.Unit { return quota.ActionsAllRunsSeconds } -func (l *databaseLogStorage) Emit(ctx context.Context, bulk []logstore.LogRecord) error { +func (l *databaseLogStorage) Emit(ctx context.Context, bulk []*record.ExecutionLog) error { if len(bulk) == 0 { return nil } - builder := squirrel.Insert(executionLogsTable). - Columns( - executionTimestampCol, - executionTookCol, - executionMessageCol, - executionLogLevelCol, - executionInstanceIdCol, - executionActionIdCol, - executionMetadataCol, - ). - PlaceholderFormat(squirrel.Dollar) + return l.incrementUsage(ctx, bulk) +} - for idx := range bulk { - item := bulk[idx].(*Record) - - var took interface{} - if item.Took > 0 { - took = item.Took +func (l *databaseLogStorage) incrementUsage(ctx context.Context, bulk []*record.ExecutionLog) (err error) { + byInstance := make(map[string][]*record.ExecutionLog) + for _, r := range bulk { + if r.InstanceID != "" { + byInstance[r.InstanceID] = append(byInstance[r.InstanceID], r) + } + } + for instanceID, instanceBulk := range byInstance { + q, getQuotaErr := l.queries.GetQuota(ctx, instanceID, quota.ActionsAllRunsSeconds) + if errors.Is(getQuotaErr, sql.ErrNoRows) { + continue + } + err = errors.Join(err, getQuotaErr) + if getQuotaErr != nil { + continue + } + sum, incrementErr := l.incrementUsageFromExecutionLogs(ctx, instanceID, q.CurrentPeriodStart, instanceBulk) + err = errors.Join(err, incrementErr) + if incrementErr != nil { + continue } - builder = builder.Values( - item.LogDate, - took, - item.Message, - item.LogLevel, - item.InstanceID, - item.ActionID, - item.Metadata, - ) + notifications, getNotificationErr := l.queries.GetDueQuotaNotifications(ctx, instanceID, quota.ActionsAllRunsSeconds, q, q.CurrentPeriodStart, sum) + err = errors.Join(err, getNotificationErr) + if getNotificationErr != nil || len(notifications) == 0 { + continue + } + ctx = authz.WithInstanceID(ctx, instanceID) + reportErr := l.commands.ReportQuotaUsage(ctx, notifications) + err = errors.Join(err, reportErr) + if reportErr != nil { + continue + } } - - stmt, args, err := builder.ToSql() - if err != nil { - return caos_errors.ThrowInternal(err, "EXEC-KOS7I", "Errors.Internal") - } - - result, err := l.dbClient.ExecContext(ctx, stmt, args...) - if err != nil { - return caos_errors.ThrowInternal(err, "EXEC-0j6i5", "Errors.Access.StorageFailed") - } - - rows, err := result.RowsAffected() - if err != nil { - return caos_errors.ThrowInternal(err, "EXEC-MGchJ", "Errors.Internal") - } - - logging.WithFields("rows", rows).Debug("successfully stored execution logs") - return nil -} - -func (l *databaseLogStorage) QueryUsage(ctx context.Context, instanceId string, start time.Time) (uint64, error) { - stmt, args, err := squirrel.Select( - fmt.Sprintf("COALESCE(SUM(%s)::INT,0)", executionTookCol), - ). - From(executionLogsTable + l.dbClient.Timetravel(call.Took(ctx))). - Where(squirrel.And{ - squirrel.Eq{executionInstanceIdCol: instanceId}, - squirrel.GtOrEq{executionTimestampCol: start}, - squirrel.NotEq{executionTookCol: nil}, - }). - PlaceholderFormat(squirrel.Dollar). - ToSql() - - if err != nil { - return 0, caos_errors.ThrowInternal(err, "EXEC-DXtzg", "Errors.Internal") - } - - var durationSeconds uint64 - err = l.dbClient. - QueryRowContext(ctx, - func(row *sql.Row) error { - return row.Scan(&durationSeconds) - }, - stmt, args..., - ) - if err != nil { - return 0, caos_errors.ThrowInternal(err, "EXEC-Ad8nP", "Errors.Logstore.Execution.ScanFailed") - } - return durationSeconds, nil -} - -func (l *databaseLogStorage) Cleanup(ctx context.Context, keep time.Duration) error { - stmt, args, err := squirrel.Delete(executionLogsTable). - Where(squirrel.LtOrEq{executionTimestampCol: time.Now().Add(-keep)}). - PlaceholderFormat(squirrel.Dollar). - ToSql() - - if err != nil { - return caos_errors.ThrowInternal(err, "EXEC-Bja8V", "Errors.Internal") - } - - execCtx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - _, err = l.dbClient.ExecContext(execCtx, stmt, args...) return err } + +func (l *databaseLogStorage) incrementUsageFromExecutionLogs(ctx context.Context, instanceID string, periodStart time.Time, records []*record.ExecutionLog) (sum uint64, err error) { + var total time.Duration + for _, r := range records { + total += r.Took + } + return projection.QuotaProjection.IncrementUsage(ctx, quota.ActionsAllRunsSeconds, instanceID, periodStart, uint64(math.Floor(total.Seconds()))) +} diff --git a/internal/logstore/emitters/mock/inmem.go b/internal/logstore/emitters/mock/inmem.go deleted file mode 100644 index 1d3043262..000000000 --- a/internal/logstore/emitters/mock/inmem.go +++ /dev/null @@ -1,89 +0,0 @@ -package mock - -import ( - "context" - "sync" - "time" - - "github.com/benbjohnson/clock" - - "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/repository/quota" -) - -var _ logstore.UsageQuerier = (*InmemLogStorage)(nil) -var _ logstore.LogCleanupper = (*InmemLogStorage)(nil) - -type InmemLogStorage struct { - mux sync.Mutex - clock clock.Clock - emitted []*record - bulks []int -} - -func NewInMemoryStorage(clock clock.Clock) *InmemLogStorage { - return &InmemLogStorage{ - clock: clock, - emitted: make([]*record, 0), - bulks: make([]int, 0), - } -} - -func (l *InmemLogStorage) QuotaUnit() quota.Unit { - return quota.Unimplemented -} - -func (l *InmemLogStorage) Emit(_ context.Context, bulk []logstore.LogRecord) error { - if len(bulk) == 0 { - return nil - } - l.mux.Lock() - defer l.mux.Unlock() - for idx := range bulk { - l.emitted = append(l.emitted, bulk[idx].(*record)) - } - l.bulks = append(l.bulks, len(bulk)) - return nil -} - -func (l *InmemLogStorage) QueryUsage(_ context.Context, _ string, start time.Time) (uint64, error) { - l.mux.Lock() - defer l.mux.Unlock() - - var count uint64 - for _, r := range l.emitted { - if r.ts.After(start) { - count++ - } - } - return count, nil -} - -func (l *InmemLogStorage) Cleanup(_ context.Context, keep time.Duration) error { - l.mux.Lock() - defer l.mux.Unlock() - - clean := make([]*record, 0) - from := l.clock.Now().Add(-(keep + 1)) - for _, r := range l.emitted { - if r.ts.After(from) { - clean = append(clean, r) - } - } - l.emitted = clean - return nil -} - -func (l *InmemLogStorage) Bulks() []int { - l.mux.Lock() - defer l.mux.Unlock() - - return l.bulks -} - -func (l *InmemLogStorage) Len() int { - l.mux.Lock() - defer l.mux.Unlock() - - return len(l.emitted) -} diff --git a/internal/logstore/emitters/stdout/stdout.go b/internal/logstore/emitters/stdout/stdout.go index 7a2cd53e6..818cc4ca1 100644 --- a/internal/logstore/emitters/stdout/stdout.go +++ b/internal/logstore/emitters/stdout/stdout.go @@ -9,8 +9,8 @@ import ( "github.com/zitadel/zitadel/internal/logstore" ) -func NewStdoutEmitter() logstore.LogEmitter { - return logstore.LogEmitterFunc(func(ctx context.Context, bulk []logstore.LogRecord) error { +func NewStdoutEmitter[T logstore.LogRecord[T]]() logstore.LogEmitter[T] { + return logstore.LogEmitterFunc[T](func(ctx context.Context, bulk []T) error { for idx := range bulk { bytes, err := json.Marshal(bulk[idx]) if err != nil { diff --git a/internal/logstore/helpers_test.go b/internal/logstore/helpers_test.go index 90348db77..6037f7058 100644 --- a/internal/logstore/helpers_test.go +++ b/internal/logstore/helpers_test.go @@ -4,16 +4,14 @@ import ( "time" "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/query" ) type emitterOption func(config *logstore.EmitterConfig) func emitterConfig(options ...emitterOption) *logstore.EmitterConfig { cfg := &logstore.EmitterConfig{ - Enabled: true, - Keep: time.Hour, - CleanupInterval: time.Hour, + Enabled: true, Debounce: &logstore.DebouncerConfig{ MinFrequency: 0, MaxBulkSize: 0, @@ -37,17 +35,10 @@ func withDisabled() emitterOption { } } -func withCleanupping(keep, interval time.Duration) emitterOption { - return func(c *logstore.EmitterConfig) { - c.Keep = keep - c.CleanupInterval = interval - } -} +type quotaOption func(config *query.Quota) -type quotaOption func(config *quota.AddedEvent) - -func quotaConfig(quotaOptions ...quotaOption) quota.AddedEvent { - q := "a.AddedEvent{ +func quotaConfig(quotaOptions ...quotaOption) *query.Quota { + q := &query.Quota{ Amount: 90, Limit: false, ResetInterval: 90 * time.Second, @@ -56,18 +47,18 @@ func quotaConfig(quotaOptions ...quotaOption) quota.AddedEvent { for _, opt := range quotaOptions { opt(q) } - return *q + return q } func withAmountAndInterval(n uint64) quotaOption { - return func(c *quota.AddedEvent) { + return func(c *query.Quota) { c.Amount = n c.ResetInterval = time.Duration(n) * time.Second } } func withLimiting() quotaOption { - return func(c *quota.AddedEvent) { + return func(c *query.Quota) { c.Limit = true } } diff --git a/internal/logstore/mock/inmem.go b/internal/logstore/mock/inmem.go new file mode 100644 index 000000000..15e8d1253 --- /dev/null +++ b/internal/logstore/mock/inmem.go @@ -0,0 +1,120 @@ +package mock + +import ( + "context" + "sync" + "time" + + "github.com/benbjohnson/clock" + + "github.com/zitadel/zitadel/internal/logstore" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +var _ logstore.UsageStorer[*Record] = (*InmemLogStorage)(nil) +var _ logstore.LogCleanupper[*Record] = (*InmemLogStorage)(nil) +var _ logstore.Queries = (*InmemLogStorage)(nil) + +type InmemLogStorage struct { + mux sync.Mutex + clock clock.Clock + emitted []*Record + bulks []int + quota *query.Quota +} + +func NewInMemoryStorage(clock clock.Clock, quota *query.Quota) *InmemLogStorage { + return &InmemLogStorage{ + clock: clock, + emitted: make([]*Record, 0), + bulks: make([]int, 0), + quota: quota, + } +} + +func (l *InmemLogStorage) QuotaUnit() quota.Unit { + return quota.Unimplemented +} + +func (l *InmemLogStorage) Emit(_ context.Context, bulk []*Record) error { + if len(bulk) == 0 { + return nil + } + l.mux.Lock() + defer l.mux.Unlock() + l.emitted = append(l.emitted, bulk...) + l.bulks = append(l.bulks, len(bulk)) + return nil +} + +func (l *InmemLogStorage) QueryUsage(_ context.Context, _ string, start time.Time) (uint64, error) { + l.mux.Lock() + defer l.mux.Unlock() + + var count uint64 + for _, r := range l.emitted { + if r.ts.After(start) { + count++ + } + } + return count, nil +} + +func (l *InmemLogStorage) Cleanup(_ context.Context, keep time.Duration) error { + l.mux.Lock() + defer l.mux.Unlock() + + clean := make([]*Record, 0) + from := l.clock.Now().Add(-(keep + 1)) + for _, r := range l.emitted { + if r.ts.After(from) { + clean = append(clean, r) + } + } + l.emitted = clean + return nil +} + +func (l *InmemLogStorage) Bulks() []int { + l.mux.Lock() + defer l.mux.Unlock() + + return l.bulks +} + +func (l *InmemLogStorage) Len() int { + l.mux.Lock() + defer l.mux.Unlock() + + return len(l.emitted) +} + +func (l *InmemLogStorage) GetQuota(ctx context.Context, instanceID string, unit quota.Unit) (qu *query.Quota, err error) { + return l.quota, nil +} + +func (l *InmemLogStorage) GetQuotaUsage(ctx context.Context, instanceID string, unit quota.Unit, periodStart time.Time) (usage uint64, err error) { + return uint64(l.Len()), nil +} + +func (l *InmemLogStorage) GetRemainingQuotaUsage(ctx context.Context, instanceID string, unit quota.Unit) (remaining *uint64, err error) { + if !l.quota.Limit { + return nil, nil + } + var r uint64 + used := uint64(l.Len()) + if used > l.quota.Amount { + return &r, nil + } + r = l.quota.Amount - used + return &r, nil +} + +func (l *InmemLogStorage) GetDueQuotaNotifications(ctx context.Context, instanceID string, unit quota.Unit, qu *query.Quota, periodStart time.Time, usedAbs uint64) (dueNotifications []*quota.NotificationDueEvent, err error) { + return nil, nil +} + +func (l *InmemLogStorage) ReportQuotaUsage(ctx context.Context, dueNotifications []*quota.NotificationDueEvent) error { + return nil +} diff --git a/internal/logstore/emitters/mock/record.go b/internal/logstore/mock/record.go similarity index 50% rename from internal/logstore/emitters/mock/record.go rename to internal/logstore/mock/record.go index be9fc6a1f..2c7a91967 100644 --- a/internal/logstore/emitters/mock/record.go +++ b/internal/logstore/mock/record.go @@ -8,18 +8,18 @@ import ( "github.com/zitadel/zitadel/internal/logstore" ) -var _ logstore.LogRecord = (*record)(nil) +var _ logstore.LogRecord[*Record] = (*Record)(nil) -func NewRecord(clock clock.Clock) *record { - return &record{ts: clock.Now()} +func NewRecord(clock clock.Clock) *Record { + return &Record{ts: clock.Now()} } -type record struct { +type Record struct { ts time.Time redacted bool } -func (r record) Normalize() logstore.LogRecord { +func (r Record) Normalize() *Record { r.redacted = true return &r } diff --git a/internal/logstore/quotaqueriers/mock/noop.go b/internal/logstore/quotaqueriers/mock/noop.go deleted file mode 100644 index 4f4335077..000000000 --- a/internal/logstore/quotaqueriers/mock/noop.go +++ /dev/null @@ -1,28 +0,0 @@ -package mock - -import ( - "context" - "time" - - "github.com/zitadel/zitadel/internal/logstore" - "github.com/zitadel/zitadel/internal/repository/quota" -) - -var _ logstore.QuotaQuerier = (*inmemReporter)(nil) - -type inmemReporter struct { - config *quota.AddedEvent - startPeriod time.Time -} - -func NewNoopQuerier(quota *quota.AddedEvent, startPeriod time.Time) *inmemReporter { - return &inmemReporter{config: quota, startPeriod: startPeriod} -} - -func (i *inmemReporter) GetCurrentQuotaPeriod(context.Context, string, quota.Unit) (*quota.AddedEvent, time.Time, error) { - return i.config, i.startPeriod, nil -} - -func (*inmemReporter) GetDueQuotaNotifications(context.Context, *quota.AddedEvent, time.Time, uint64) ([]*quota.NotificationDueEvent, error) { - return nil, nil -} diff --git a/internal/logstore/record/access.go b/internal/logstore/record/access.go new file mode 100644 index 000000000..8f9b17bb8 --- /dev/null +++ b/internal/logstore/record/access.go @@ -0,0 +1,135 @@ +package record + +import ( + "net/http" + "strings" + "time" + + "google.golang.org/grpc/codes" + + zitadel_http "github.com/zitadel/zitadel/internal/api/http" +) + +type AccessLog struct { + LogDate time.Time `json:"logDate"` + Protocol AccessProtocol `json:"protocol"` + RequestURL string `json:"requestUrl"` + ResponseStatus uint32 `json:"responseStatus"` + // RequestHeaders and ResponseHeaders are plain maps so varying implementations + // between HTTP and gRPC don't interfere with each other + RequestHeaders map[string][]string `json:"requestHeaders"` + ResponseHeaders map[string][]string `json:"responseHeaders"` + InstanceID string `json:"instanceId"` + ProjectID string `json:"projectId"` + RequestedDomain string `json:"requestedDomain"` + RequestedHost string `json:"requestedHost"` + // NotCountable can be used by the logging service to explicitly stating, + // that the request must not increase the amount of countable (authenticated) requests + NotCountable bool `json:"-"` + normalized bool `json:"-"` +} + +type AccessProtocol uint8 + +const ( + GRPC AccessProtocol = iota + HTTP + + redacted = "[REDACTED]" +) + +var ( + unaccountableEndpoints = []string{ + "/zitadel.system.v1.SystemService/", + "/zitadel.admin.v1.AdminService/Healthz", + "/zitadel.management.v1.ManagementService/Healthz", + "/zitadel.management.v1.ManagementService/GetOIDCInformation", + "/zitadel.auth.v1.AuthService/Healthz", + } +) + +func (a AccessLog) IsAuthenticated() bool { + if a.NotCountable { + return false + } + if !a.normalized { + panic("access log not normalized, Normalize() must be called before IsAuthenticated()") + } + _, hasHTTPAuthHeader := a.RequestHeaders[strings.ToLower(zitadel_http.Authorization)] + // ignore requests, which were unauthorized or do not require an authorization (even if one was sent) + // also ignore if the limit was already reached or if the server returned an internal error + // not that endpoints paths are only checked with the gRPC representation as HTTP (gateway) will not log them + return hasHTTPAuthHeader && + (a.Protocol == HTTP && + a.ResponseStatus != http.StatusInternalServerError && + a.ResponseStatus != http.StatusTooManyRequests && + a.ResponseStatus != http.StatusUnauthorized) || + (a.Protocol == GRPC && + a.ResponseStatus != uint32(codes.Internal) && + a.ResponseStatus != uint32(codes.ResourceExhausted) && + a.ResponseStatus != uint32(codes.Unauthenticated) && + !a.isUnaccountableEndpoint()) +} + +func (a AccessLog) isUnaccountableEndpoint() bool { + for _, endpoint := range unaccountableEndpoints { + if strings.HasPrefix(a.RequestURL, endpoint) { + return true + } + } + return false +} + +func (a AccessLog) Normalize() *AccessLog { + a.RequestedDomain = cutString(a.RequestedDomain, 200) + a.RequestURL = cutString(a.RequestURL, 200) + a.RequestHeaders = normalizeHeaders(a.RequestHeaders, strings.ToLower(zitadel_http.Authorization), "grpcgateway-authorization", "cookie", "grpcgateway-cookie") + a.ResponseHeaders = normalizeHeaders(a.ResponseHeaders, "set-cookie") + a.normalized = true + return &a +} + +// normalizeHeaders lowers all header keys and redacts secrets +func normalizeHeaders(header map[string][]string, redactKeysLower ...string) map[string][]string { + return pruneKeys(redactKeys(lowerKeys(header), redactKeysLower...)) +} + +func lowerKeys(header map[string][]string) map[string][]string { + lower := make(map[string][]string, len(header)) + for k, v := range header { + lower[strings.ToLower(k)] = v + } + return lower +} + +func redactKeys(header map[string][]string, redactKeysLower ...string) map[string][]string { + redactedKeys := make(map[string][]string, len(header)) + for k, v := range header { + redactedKeys[k] = v + } + for _, redactKey := range redactKeysLower { + if _, ok := redactedKeys[redactKey]; ok { + redactedKeys[redactKey] = []string{redacted} + } + } + return redactedKeys +} + +const maxValuesPerKey = 10 + +func pruneKeys(header map[string][]string) map[string][]string { + prunedKeys := make(map[string][]string, len(header)) + for key, value := range header { + valueItems := make([]string, 0, maxValuesPerKey) + for i, valueItem := range value { + // Max 10 header values per key + if i > maxValuesPerKey { + break + } + // Max 200 value length + valueItems = append(valueItems, cutString(valueItem, 200)) + } + prunedKeys[key] = valueItems + } + return prunedKeys +} diff --git a/internal/logstore/emitters/access/record_test.go b/internal/logstore/record/access_test.go similarity index 85% rename from internal/logstore/emitters/access/record_test.go rename to internal/logstore/record/access_test.go index 40cc835be..23ffff515 100644 --- a/internal/logstore/emitters/access/record_test.go +++ b/internal/logstore/record/access_test.go @@ -1,20 +1,18 @@ -package access_test +package record import ( "reflect" "testing" - - "github.com/zitadel/zitadel/internal/logstore/emitters/access" ) func TestRecord_Normalize(t *testing.T) { tests := []struct { name string - record access.Record - want *access.Record + record AccessLog + want *AccessLog }{{ name: "headers with certain keys should be redacted", - record: access.Record{ + record: AccessLog{ RequestHeaders: map[string][]string{ "authorization": {"AValue"}, "grpcgateway-authorization": {"AValue"}, @@ -24,7 +22,7 @@ func TestRecord_Normalize(t *testing.T) { "set-cookie": {"AValue"}, }, }, - want: &access.Record{ + want: &AccessLog{ RequestHeaders: map[string][]string{ "authorization": {"[REDACTED]"}, "grpcgateway-authorization": {"[REDACTED]"}, @@ -36,22 +34,22 @@ func TestRecord_Normalize(t *testing.T) { }, }, { name: "header keys should be lower cased", - record: access.Record{ + record: AccessLog{ RequestHeaders: map[string][]string{"AKey": {"AValue"}}, ResponseHeaders: map[string][]string{"AKey": {"AValue"}}}, - want: &access.Record{ + want: &AccessLog{ RequestHeaders: map[string][]string{"akey": {"AValue"}}, ResponseHeaders: map[string][]string{"akey": {"AValue"}}}, }, { name: "an already prune record should stay unchanged", - record: access.Record{ + record: AccessLog{ RequestURL: "https://my.zitadel.cloud/", RequestHeaders: map[string][]string{ "authorization": {"[REDACTED]"}, }, ResponseHeaders: map[string][]string{}, }, - want: &access.Record{ + want: &AccessLog{ RequestURL: "https://my.zitadel.cloud/", RequestHeaders: map[string][]string{ "authorization": {"[REDACTED]"}, @@ -60,17 +58,18 @@ func TestRecord_Normalize(t *testing.T) { }, }, { name: "empty record should stay empty", - record: access.Record{ + record: AccessLog{ RequestHeaders: map[string][]string{}, ResponseHeaders: map[string][]string{}, }, - want: &access.Record{ + want: &AccessLog{ RequestHeaders: map[string][]string{}, ResponseHeaders: map[string][]string{}, }, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.want.normalized = true if got := tt.record.Normalize(); !reflect.DeepEqual(got, tt.want) { t.Errorf("Normalize() = %v, want %v", got, tt.want) } diff --git a/internal/logstore/emitters/execution/record.go b/internal/logstore/record/execution.go similarity index 63% rename from internal/logstore/emitters/execution/record.go rename to internal/logstore/record/execution.go index c45790622..ee53cbeab 100644 --- a/internal/logstore/emitters/execution/record.go +++ b/internal/logstore/record/execution.go @@ -1,16 +1,12 @@ -package execution +package record import ( "time" "github.com/sirupsen/logrus" - - "github.com/zitadel/zitadel/internal/logstore" ) -var _ logstore.LogRecord = (*Record)(nil) - -type Record struct { +type ExecutionLog struct { LogDate time.Time `json:"logDate"` Took time.Duration `json:"took"` Message string `json:"message"` @@ -20,14 +16,7 @@ type Record struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } -func (e Record) Normalize() logstore.LogRecord { +func (e ExecutionLog) Normalize() *ExecutionLog { e.Message = cutString(e.Message, 2000) return &e } - -func cutString(str string, pos int) string { - if len(str) <= pos { - return str - } - return str[:pos] -} diff --git a/internal/logstore/record/prune.go b/internal/logstore/record/prune.go new file mode 100644 index 000000000..47e08343b --- /dev/null +++ b/internal/logstore/record/prune.go @@ -0,0 +1,8 @@ +package record + +func cutString(str string, pos int) string { + if len(str) <= pos { + return str + } + return str[:pos-1] +} diff --git a/internal/logstore/service.go b/internal/logstore/service.go index 542b50dd5..03620336e 100644 --- a/internal/logstore/service.go +++ b/internal/logstore/service.go @@ -2,124 +2,70 @@ package logstore import ( "context" - "math" - "time" "github.com/zitadel/logging" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/repository/quota" ) -const handleThresholdTimeout = time.Minute - -type QuotaQuerier interface { - GetCurrentQuotaPeriod(ctx context.Context, instanceID string, unit quota.Unit) (config *quota.AddedEvent, periodStart time.Time, err error) - GetDueQuotaNotifications(ctx context.Context, config *quota.AddedEvent, periodStart time.Time, used uint64) ([]*quota.NotificationDueEvent, error) -} - -type UsageQuerier interface { - LogEmitter +type UsageStorer[T LogRecord[T]] interface { + LogEmitter[T] QuotaUnit() quota.Unit - QueryUsage(ctx context.Context, instanceId string, start time.Time) (uint64, error) } -type UsageReporter interface { - Report(ctx context.Context, notifications []*quota.NotificationDueEvent) (err error) -} - -type UsageReporterFunc func(context.Context, []*quota.NotificationDueEvent) (err error) - -func (u UsageReporterFunc) Report(ctx context.Context, notifications []*quota.NotificationDueEvent) (err error) { - return u(ctx, notifications) -} - -type Service struct { - usageQuerier UsageQuerier - quotaQuerier QuotaQuerier - usageReporter UsageReporter - enabledSinks []*emitter +type Service[T LogRecord[T]] struct { + queries Queries + usageStorer UsageStorer[T] + enabledSinks []*emitter[T] sinkEnabled bool reportingEnabled bool } -func New(quotaQuerier QuotaQuerier, usageReporter UsageReporter, usageQuerierSink *emitter, additionalSink ...*emitter) *Service { - var usageQuerier UsageQuerier +type Queries interface { + GetRemainingQuotaUsage(ctx context.Context, instanceID string, unit quota.Unit) (remaining *uint64, err error) +} + +func New[T LogRecord[T]](queries Queries, usageQuerierSink *emitter[T], additionalSink ...*emitter[T]) *Service[T] { + var usageStorer UsageStorer[T] if usageQuerierSink != nil { - usageQuerier = usageQuerierSink.emitter.(UsageQuerier) + usageStorer = usageQuerierSink.emitter.(UsageStorer[T]) } - - svc := &Service{ + svc := &Service[T]{ + queries: queries, reportingEnabled: usageQuerierSink != nil && usageQuerierSink.enabled, - usageQuerier: usageQuerier, - quotaQuerier: quotaQuerier, - usageReporter: usageReporter, + usageStorer: usageStorer, } - - for _, s := range append([]*emitter{usageQuerierSink}, additionalSink...) { + for _, s := range append([]*emitter[T]{usageQuerierSink}, additionalSink...) { if s != nil && s.enabled { svc.enabledSinks = append(svc.enabledSinks, s) } } - svc.sinkEnabled = len(svc.enabledSinks) > 0 - return svc } -func (s *Service) Enabled() bool { +func (s *Service[T]) Enabled() bool { return s.sinkEnabled } -func (s *Service) Handle(ctx context.Context, record LogRecord) { +func (s *Service[T]) Handle(ctx context.Context, record T) { for _, sink := range s.enabledSinks { logging.OnError(sink.Emit(ctx, record.Normalize())).WithField("record", record).Warn("failed to emit log record") } } -func (s *Service) Limit(ctx context.Context, instanceID string) *uint64 { +func (s *Service[T]) Limit(ctx context.Context, instanceID string) *uint64 { var err error defer func() { - logging.OnError(err).Warn("failed to check is usage should be limited") + logging.OnError(err).Warn("failed to check if usage should be limited") }() - if !s.reportingEnabled || instanceID == "" { return nil } - - quota, periodStart, err := s.quotaQuerier.GetCurrentQuotaPeriod(ctx, instanceID, s.usageQuerier.QuotaUnit()) - if err != nil || quota == nil { - return nil - } - - usage, err := s.usageQuerier.QueryUsage(ctx, instanceID, periodStart) + remaining, err := s.queries.GetRemainingQuotaUsage(ctx, instanceID, s.usageStorer.QuotaUnit()) if err != nil { + // TODO: shouldn't we just limit then or return the error and decide there? return nil } - - go s.handleThresholds(ctx, quota, periodStart, usage) - - var remaining *uint64 - if quota.Limit { - r := uint64(math.Max(0, float64(quota.Amount)-float64(usage))) - remaining = &r - } return remaining } - -func (s *Service) handleThresholds(ctx context.Context, quota *quota.AddedEvent, periodStart time.Time, usage uint64) { - var err error - defer func() { - logging.OnError(err).Warn("handling quota thresholds failed") - }() - - detatchedCtx, cancel := context.WithTimeout(authz.Detach(ctx), handleThresholdTimeout) - defer cancel() - - notifications, err := s.quotaQuerier.GetDueQuotaNotifications(detatchedCtx, quota, periodStart, usage) - if err != nil || len(notifications) == 0 { - return - } - - err = s.usageReporter.Report(detatchedCtx, notifications) -} diff --git a/internal/logstore/service_test.go b/internal/logstore/service_test.go index ef6fcd128..d99e50161 100644 --- a/internal/logstore/service_test.go +++ b/internal/logstore/service_test.go @@ -14,9 +14,8 @@ import ( "github.com/benbjohnson/clock" "github.com/zitadel/zitadel/internal/logstore" - emittermock "github.com/zitadel/zitadel/internal/logstore/emitters/mock" - quotaqueriermock "github.com/zitadel/zitadel/internal/logstore/quotaqueriers/mock" - "github.com/zitadel/zitadel/internal/repository/quota" + emittermock "github.com/zitadel/zitadel/internal/logstore/mock" + "github.com/zitadel/zitadel/internal/query" ) const ( @@ -27,7 +26,7 @@ const ( type args struct { mainSink *logstore.EmitterConfig secondarySink *logstore.EmitterConfig - config quota.AddedEvent + config *query.Quota } type want struct { @@ -137,28 +136,6 @@ func TestService(t *testing.T) { len: 0, }, }, - }, { - name: "cleanupping works", - args: args{ - mainSink: emitterConfig(withCleanupping(17*time.Second, 28*time.Second)), - secondarySink: emitterConfig(withDebouncerConfig(&logstore.DebouncerConfig{ - MinFrequency: 0, - MaxBulkSize: 15, - }), withCleanupping(5*time.Second, 47*time.Second)), - config: quotaConfig(), - }, - want: want{ - enabled: true, - remaining: nil, - mainSink: wantSink{ - bulks: repeat(1, 60), - len: 21, - }, - secondarySink: wantSink{ - bulks: repeat(15, 4), - len: 18, - }, - }, }, { name: "when quota has a limit of 90, 30 are remaining", args: args{ @@ -232,27 +209,24 @@ func runTest(t *testing.T, name string, args args, want want) bool { }) } -func given(t *testing.T, args args, want want) (context.Context, *clock.Mock, *emittermock.InmemLogStorage, *emittermock.InmemLogStorage, *logstore.Service) { +func given(t *testing.T, args args, want want) (context.Context, *clock.Mock, *emittermock.InmemLogStorage, *emittermock.InmemLogStorage, *logstore.Service[*emittermock.Record]) { ctx := context.Background() clock := clock.NewMock() - periodStart := time.Time{} clock.Set(args.config.From) - mainStorage := emittermock.NewInMemoryStorage(clock) - mainEmitter, err := logstore.NewEmitter(ctx, clock, args.mainSink, mainStorage) + mainStorage := emittermock.NewInMemoryStorage(clock, args.config) + mainEmitter, err := logstore.NewEmitter[*emittermock.Record](ctx, clock, args.mainSink, mainStorage) if err != nil { t.Errorf("expected no error but got %v", err) } - secondaryStorage := emittermock.NewInMemoryStorage(clock) - secondaryEmitter, err := logstore.NewEmitter(ctx, clock, args.secondarySink, secondaryStorage) + secondaryStorage := emittermock.NewInMemoryStorage(clock, args.config) + secondaryEmitter, err := logstore.NewEmitter[*emittermock.Record](ctx, clock, args.secondarySink, secondaryStorage) if err != nil { t.Errorf("expected no error but got %v", err) } - - svc := logstore.New( - quotaqueriermock.NewNoopQuerier(&args.config, periodStart), - logstore.UsageReporterFunc(func(context.Context, []*quota.NotificationDueEvent) error { return nil }), + svc := logstore.New[*emittermock.Record]( + mainStorage, mainEmitter, secondaryEmitter) @@ -262,7 +236,7 @@ func given(t *testing.T, args args, want want) (context.Context, *clock.Mock, *e return ctx, clock, mainStorage, secondaryStorage, svc } -func when(svc *logstore.Service, ctx context.Context, clock *clock.Mock) *uint64 { +func when(svc *logstore.Service[*emittermock.Record], ctx context.Context, clock *clock.Mock) *uint64 { var remaining *uint64 for i := 0; i < ticks; i++ { svc.Handle(ctx, emittermock.NewRecord(clock)) diff --git a/cmd/start/start_port.go b/internal/net/start_port.go similarity index 58% rename from cmd/start/start_port.go rename to internal/net/start_port.go index bb60fea25..df52540e5 100644 --- a/cmd/start/start_port.go +++ b/internal/net/start_port.go @@ -1,11 +1,11 @@ //go:build !integration -package start +package net import ( "net" ) -func listenConfig() *net.ListenConfig { +func ListenConfig() *net.ListenConfig { return &net.ListenConfig{} } diff --git a/cmd/start/start_port_integration.go b/internal/net/start_port_integration.go similarity index 87% rename from cmd/start/start_port_integration.go rename to internal/net/start_port_integration.go index 1c5763d6e..d2e7e5802 100644 --- a/cmd/start/start_port_integration.go +++ b/internal/net/start_port_integration.go @@ -1,6 +1,6 @@ //go:build integration -package start +package net import ( "net" @@ -9,7 +9,7 @@ import ( "golang.org/x/sys/unix" ) -func listenConfig() *net.ListenConfig { +func ListenConfig() *net.ListenConfig { return &net.ListenConfig{ Control: reusePort, } diff --git a/internal/notification/handlers/handlers_integration_test.go b/internal/notification/handlers/handlers_integration_test.go index 37f6a344c..609f01b3f 100644 --- a/internal/notification/handlers/handlers_integration_test.go +++ b/internal/notification/handlers/handlers_integration_test.go @@ -20,11 +20,13 @@ var ( func TestMain(m *testing.M) { os.Exit(func() int { ctx, _, cancel := integration.Contexts(5 * time.Minute) - CTX = ctx defer cancel() + CTX = ctx + Tester = integration.NewTester(ctx) - SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser) defer Tester.Done() + + SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser) return m.Run() }()) } diff --git a/internal/notification/handlers/telemetry_pusher_integration_test.go b/internal/notification/handlers/telemetry_pusher_integration_test.go index e3709db17..f0d46b361 100644 --- a/internal/notification/handlers/telemetry_pusher_integration_test.go +++ b/internal/notification/handlers/telemetry_pusher_integration_test.go @@ -5,11 +5,6 @@ package handlers_test import ( "bytes" "encoding/json" - "io" - "net" - "net/http" - "net/http/httptest" - "reflect" "testing" "time" @@ -18,49 +13,27 @@ import ( ) func TestServer_TelemetryPushMilestones(t *testing.T) { - bodies := make(chan []byte, 0) - mockServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - t.Error(err) - } - if r.Header.Get("single-value") != "single-value" { - t.Error("single-value header not set") - } - if reflect.DeepEqual(r.Header.Get("multi-value"), "multi-value-1,multi-value-2") { - t.Error("single-value header not set") - } - bodies <- body - w.WriteHeader(http.StatusOK) - })) - listener, err := net.Listen("tcp", "localhost:8081") - if err != nil { - t.Fatal(err) - } - mockServer.Listener = listener - mockServer.Start() - t.Cleanup(mockServer.Close) primaryDomain, instanceID, iamOwnerCtx := Tester.UseIsolatedInstance(CTX, SystemCTX) t.Log("testing against instance with primary domain", primaryDomain) - awaitMilestone(t, bodies, primaryDomain, "InstanceCreated") + awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "InstanceCreated") project, err := Tester.Client.Mgmt.AddProject(iamOwnerCtx, &management.AddProjectRequest{Name: "integration"}) if err != nil { t.Fatal(err) } - awaitMilestone(t, bodies, primaryDomain, "ProjectCreated") + awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "ProjectCreated") if _, err = Tester.Client.Mgmt.AddOIDCApp(iamOwnerCtx, &management.AddOIDCAppRequest{ ProjectId: project.GetId(), Name: "integration", }); err != nil { t.Fatal(err) } - awaitMilestone(t, bodies, primaryDomain, "ApplicationCreated") + awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "ApplicationCreated") // TODO: trigger and await milestone AuthenticationSucceededOnInstance // TODO: trigger and await milestone AuthenticationSucceededOnApplication if _, err = Tester.Client.System.RemoveInstance(SystemCTX, &system.RemoveInstanceRequest{InstanceId: instanceID}); err != nil { t.Fatal(err) } - awaitMilestone(t, bodies, primaryDomain, "InstanceDeleted") + awaitMilestone(t, Tester.MilestoneChan, primaryDomain, "InstanceDeleted") } func awaitMilestone(t *testing.T, bodies chan []byte, primaryDomain, expectMilestoneType string) { diff --git a/internal/query/projection/main_test.go b/internal/query/projection/main_test.go index 1c09cf3ff..2cf61fe54 100644 --- a/internal/query/projection/main_test.go +++ b/internal/query/projection/main_test.go @@ -13,6 +13,7 @@ import ( key_repo "github.com/zitadel/zitadel/internal/repository/keypair" "github.com/zitadel/zitadel/internal/repository/org" proj_repo "github.com/zitadel/zitadel/internal/repository/project" + quota_repo "github.com/zitadel/zitadel/internal/repository/quota" usr_repo "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/repository/usergrant" ) @@ -29,6 +30,7 @@ func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore { org.RegisterEventMappers(es) usr_repo.RegisterEventMappers(es) proj_repo.RegisterEventMappers(es) + quota_repo.RegisterEventMappers(es) usergrant.RegisterEventMappers(es) key_repo.RegisterEventMappers(es) action_repo.RegisterEventMappers(es) diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index e8f2bd156..19a0c1cc0 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -69,6 +69,7 @@ var ( SessionProjection *sessionProjection AuthRequestProjection *authRequestProjection MilestoneProjection *milestoneProjection + QuotaProjection *quotaProjection ) type projection interface { @@ -148,6 +149,7 @@ func Create(ctx context.Context, sqlClient *database.DB, es *eventstore.Eventsto SessionProjection = newSessionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sessions"])) AuthRequestProjection = newAuthRequestProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["auth_requests"])) MilestoneProjection = newMilestoneProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["milestones"])) + QuotaProjection = newQuotaProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["quotas"])) newProjectionsList() return nil } @@ -247,5 +249,6 @@ func newProjectionsList() { SessionProjection, AuthRequestProjection, MilestoneProjection, + QuotaProjection, } } diff --git a/internal/query/projection/quota.go b/internal/query/projection/quota.go new file mode 100644 index 000000000..fd6e12266 --- /dev/null +++ b/internal/query/projection/quota.go @@ -0,0 +1,285 @@ +package projection + +import ( + "context" + "time" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +const ( + QuotasProjectionTable = "projections.quotas" + QuotaPeriodsProjectionTable = QuotasProjectionTable + "_" + quotaPeriodsTableSuffix + QuotaNotificationsTable = QuotasProjectionTable + "_" + quotaNotificationsTableSuffix + + QuotaColumnID = "id" + QuotaColumnInstanceID = "instance_id" + QuotaColumnUnit = "unit" + QuotaColumnAmount = "amount" + QuotaColumnFrom = "from_anchor" + QuotaColumnInterval = "interval" + QuotaColumnLimit = "limit_usage" + + quotaPeriodsTableSuffix = "periods" + QuotaPeriodColumnInstanceID = "instance_id" + QuotaPeriodColumnUnit = "unit" + QuotaPeriodColumnStart = "start" + QuotaPeriodColumnUsage = "usage" + + quotaNotificationsTableSuffix = "notifications" + QuotaNotificationColumnInstanceID = "instance_id" + QuotaNotificationColumnUnit = "unit" + QuotaNotificationColumnID = "id" + QuotaNotificationColumnCallURL = "call_url" + QuotaNotificationColumnPercent = "percent" + QuotaNotificationColumnRepeat = "repeat" + QuotaNotificationColumnLatestDuePeriodStart = "latest_due_period_start" + QuotaNotificationColumnNextDueThreshold = "next_due_threshold" +) + +const ( + incrementQuotaStatement = `INSERT INTO projections.quotas_periods` + + ` (instance_id, unit, start, usage)` + + ` VALUES ($1, $2, $3, $4) ON CONFLICT (instance_id, unit, start)` + + ` DO UPDATE SET usage = projections.quotas_periods.usage + excluded.usage RETURNING usage` +) + +type quotaProjection struct { + crdb.StatementHandler + client *database.DB +} + +func newQuotaProjection(ctx context.Context, config crdb.StatementHandlerConfig) *quotaProjection { + p := new(quotaProjection) + config.ProjectionName = QuotasProjectionTable + config.Reducers = p.reducers() + config.InitCheck = crdb.NewMultiTableCheck( + crdb.NewTable( + []*crdb.Column{ + crdb.NewColumn(QuotaColumnID, crdb.ColumnTypeText), + crdb.NewColumn(QuotaColumnInstanceID, crdb.ColumnTypeText), + crdb.NewColumn(QuotaColumnUnit, crdb.ColumnTypeEnum), + crdb.NewColumn(QuotaColumnAmount, crdb.ColumnTypeInt64), + crdb.NewColumn(QuotaColumnFrom, crdb.ColumnTypeTimestamp), + crdb.NewColumn(QuotaColumnInterval, crdb.ColumnTypeInterval), + crdb.NewColumn(QuotaColumnLimit, crdb.ColumnTypeBool), + }, + crdb.NewPrimaryKey(QuotaColumnInstanceID, QuotaColumnUnit), + ), + crdb.NewSuffixedTable( + []*crdb.Column{ + crdb.NewColumn(QuotaPeriodColumnInstanceID, crdb.ColumnTypeText), + crdb.NewColumn(QuotaPeriodColumnUnit, crdb.ColumnTypeEnum), + crdb.NewColumn(QuotaPeriodColumnStart, crdb.ColumnTypeTimestamp), + crdb.NewColumn(QuotaPeriodColumnUsage, crdb.ColumnTypeInt64), + }, + crdb.NewPrimaryKey(QuotaPeriodColumnInstanceID, QuotaPeriodColumnUnit, QuotaPeriodColumnStart), + quotaPeriodsTableSuffix, + ), + crdb.NewSuffixedTable( + []*crdb.Column{ + crdb.NewColumn(QuotaNotificationColumnInstanceID, crdb.ColumnTypeText), + crdb.NewColumn(QuotaNotificationColumnUnit, crdb.ColumnTypeEnum), + crdb.NewColumn(QuotaNotificationColumnID, crdb.ColumnTypeText), + crdb.NewColumn(QuotaNotificationColumnCallURL, crdb.ColumnTypeText), + crdb.NewColumn(QuotaNotificationColumnPercent, crdb.ColumnTypeInt64), + crdb.NewColumn(QuotaNotificationColumnRepeat, crdb.ColumnTypeBool), + crdb.NewColumn(QuotaNotificationColumnLatestDuePeriodStart, crdb.ColumnTypeTimestamp, crdb.Nullable()), + crdb.NewColumn(QuotaNotificationColumnNextDueThreshold, crdb.ColumnTypeInt64, crdb.Nullable()), + }, + crdb.NewPrimaryKey(QuotaNotificationColumnInstanceID, QuotaNotificationColumnUnit, QuotaNotificationColumnID), + quotaNotificationsTableSuffix, + ), + ) + p.StatementHandler = crdb.NewStatementHandler(ctx, config) + p.client = config.Client + return p +} + +func (q *quotaProjection) reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: instance.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: instance.InstanceRemovedEventType, + Reduce: q.reduceInstanceRemoved, + }, + }, + }, + { + Aggregate: quota.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: quota.AddedEventType, + Reduce: q.reduceQuotaAdded, + }, + }, + }, + { + Aggregate: quota.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: quota.RemovedEventType, + Reduce: q.reduceQuotaRemoved, + }, + }, + }, + { + Aggregate: quota.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: quota.NotificationDueEventType, + Reduce: q.reduceQuotaNotificationDue, + }, + }, + }, + { + Aggregate: quota.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: quota.NotifiedEventType, + Reduce: q.reduceQuotaNotified, + }, + }, + }, + } +} + +func (q *quotaProjection) reduceQuotaNotified(event eventstore.Event) (*handler.Statement, error) { + return crdb.NewNoOpStatement(event), nil +} + +func (q *quotaProjection) reduceQuotaAdded(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*quota.AddedEvent](event) + if err != nil { + return nil, err + } + + createStatements := make([]func(e eventstore.Event) crdb.Exec, len(e.Notifications)+1) + createStatements[0] = crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(QuotaColumnID, e.Aggregate().ID), + handler.NewCol(QuotaColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCol(QuotaColumnUnit, e.Unit), + handler.NewCol(QuotaColumnAmount, e.Amount), + handler.NewCol(QuotaColumnFrom, e.From), + handler.NewCol(QuotaColumnInterval, e.ResetInterval), + handler.NewCol(QuotaColumnLimit, e.Limit), + }) + for i := range e.Notifications { + notification := e.Notifications[i] + createStatements[i+1] = crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCol(QuotaNotificationColumnUnit, e.Unit), + handler.NewCol(QuotaNotificationColumnID, notification.ID), + handler.NewCol(QuotaNotificationColumnCallURL, notification.CallURL), + handler.NewCol(QuotaNotificationColumnPercent, notification.Percent), + handler.NewCol(QuotaNotificationColumnRepeat, notification.Repeat), + }, + crdb.WithTableSuffix(quotaNotificationsTableSuffix), + ) + } + + return crdb.NewMultiStatement(e, createStatements...), nil +} + +func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*quota.NotificationDueEvent](event) + if err != nil { + return nil, err + } + return crdb.NewUpdateStatement(e, + []handler.Column{ + handler.NewCol(QuotaNotificationColumnLatestDuePeriodStart, e.PeriodStart), + handler.NewCol(QuotaNotificationColumnNextDueThreshold, e.Threshold+100), // next due_threshold is always the reached + 100 => percent (e.g. 90) in the next bucket (e.g. 190) + }, + []handler.Condition{ + handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCond(QuotaNotificationColumnUnit, e.Unit), + handler.NewCond(QuotaNotificationColumnID, e.ID), + }, + crdb.WithTableSuffix(quotaNotificationsTableSuffix), + ), nil +} + +func (q *quotaProjection) reduceQuotaRemoved(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*quota.RemovedEvent](event) + if err != nil { + return nil, err + } + return crdb.NewMultiStatement( + e, + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaPeriodColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCond(QuotaPeriodColumnUnit, e.Unit), + }, + crdb.WithTableSuffix(quotaPeriodsTableSuffix), + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCond(QuotaNotificationColumnUnit, e.Unit), + }, + crdb.WithTableSuffix(quotaNotificationsTableSuffix), + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCond(QuotaColumnUnit, e.Unit), + }, + ), + ), nil +} + +func (q *quotaProjection) reduceInstanceRemoved(event eventstore.Event) (*handler.Statement, error) { + // we only assert the event to make sure it is the correct type + e, err := assertEvent[*instance.InstanceRemovedEvent](event) + if err != nil { + return nil, err + } + return crdb.NewMultiStatement( + e, + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaPeriodColumnInstanceID, e.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(quotaPeriodsTableSuffix), + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(quotaNotificationsTableSuffix), + ), + crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaColumnInstanceID, e.Aggregate().InstanceID), + }, + ), + ), nil +} + +func (q *quotaProjection) IncrementUsage(ctx context.Context, unit quota.Unit, instanceID string, periodStart time.Time, count uint64) (sum uint64, err error) { + if count == 0 { + return 0, nil + } + + err = q.client.DB.QueryRowContext( + ctx, + incrementQuotaStatement, + instanceID, unit, periodStart, count, + ).Scan(&sum) + if err != nil { + return 0, errors.ThrowInternalf(err, "PROJ-SJL3h", "incrementing usage for unit %d failed for at least one quota period", unit) + } + return sum, err +} diff --git a/internal/query/projection/quota_test.go b/internal/query/projection/quota_test.go new file mode 100644 index 000000000..dc9184da0 --- /dev/null +++ b/internal/query/projection/quota_test.go @@ -0,0 +1,321 @@ +package projection + +import ( + "context" + "regexp" + "testing" + "time" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +func TestQuotasProjection_reduces(t *testing.T) { + type args struct { + event func(t *testing.T) eventstore.Event + } + tests := []struct { + name string + args args + reduce func(event eventstore.Event) (*handler.Statement, error) + want wantReduce + }{ + { + name: "reduceQuotaAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.AddedEventType), + quota.AggregateType, + []byte(`{ + "unit": 1, + "amount": 10, + "limit": true, + "from": "2023-01-01T00:00:00Z", + "interval": 300000000000 + }`), + ), quota.AddedEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.quotas (id, instance_id, unit, amount, from_anchor, interval, limit_usage) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, + uint64(10), + time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), + time.Minute * 5, + true, + }, + }, + }, + }, + }, + }, + { + name: "reduceQuotaAdded with notification", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.AddedEventType), + quota.AggregateType, + []byte(`{ + "unit": 1, + "amount": 10, + "limit": true, + "from": "2023-01-01T00:00:00Z", + "interval": 300000000000, + "notifications": [ + { + "id": "id", + "percent": 100, + "repeat": true, + "callURL": "url" + } + ] + }`), + ), quota.AddedEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.quotas (id, instance_id, unit, amount, from_anchor, interval, limit_usage) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, + uint64(10), + time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), + time.Minute * 5, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.quotas_notifications (instance_id, unit, id, call_url, percent, repeat) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + "id", + "url", + uint16(100), + true, + }, + }, + }, + }, + }, + }, + { + name: "reduceQuotaNotificationDue", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.NotificationDueEventType), + quota.AggregateType, + []byte(`{ + "id": "id", + "unit": 1, + "callURL": "url", + "periodStart": "2023-01-01T00:00:00Z", + "threshold": 200, + "usage": 100 + }`), + ), quota.NotificationDueEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaNotificationDue, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.quotas_notifications SET (latest_due_period_start, next_due_threshold) = ($1, $2) WHERE (instance_id = $3) AND (unit = $4) AND (id = $5)", + expectedArgs: []interface{}{ + time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), + uint16(300), + "instance-id", + quota.RequestsAllAuthenticated, + "id", + }, + }, + }, + }, + }, + }, + { + name: "reduceQuotaRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.RemovedEventType), + quota.AggregateType, + []byte(`{ + "unit": 1 + }`), + ), quota.RemovedEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM projections.quotas_periods WHERE (instance_id = $1) AND (unit = $2)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas_notifications WHERE (instance_id = $1) AND (unit = $2)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas WHERE (instance_id = $1) AND (unit = $2)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + }, + }, + }, + }, { + name: "reduceInstanceRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.InstanceRemovedEventType), + instance.AggregateType, + []byte(`{ + "name": "name" + }`), + ), instance.InstanceRemovedEventMapper), + }, + reduce: ("aProjection{}).reduceInstanceRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM projections.quotas_periods WHERE (instance_id = $1)", + expectedArgs: []interface{}{ + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas_notifications WHERE (instance_id = $1)", + expectedArgs: []interface{}{ + "instance-id", + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas WHERE (instance_id = $1)", + expectedArgs: []interface{}{ + "instance-id", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + event := baseEvent(t) + got, err := tt.reduce(event) + if !errors.IsErrorInvalidArgument(err) { + t.Errorf("no wrong event mapping: %v, got: %v", err, got) + } + event = tt.args.event(t) + got, err = tt.reduce(event) + assertReduce(t, got, err, QuotasProjectionTable, tt.want) + }) + } +} + +func Test_quotaProjection_IncrementUsage(t *testing.T) { + testNow := time.Now() + type fields struct { + client *database.DB + } + type args struct { + ctx context.Context + unit quota.Unit + instanceID string + periodStart time.Time + count uint64 + } + type res struct { + sum uint64 + err error + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "", + fields: fields{ + client: func() *database.DB { + db, mock, _ := sqlmock.New() + mock.ExpectQuery(regexp.QuoteMeta(incrementQuotaStatement)). + WithArgs( + "instance_id", + 1, + testNow, + 2, + ). + WillReturnRows(sqlmock.NewRows([]string{"key"}). + AddRow(3)) + return &database.DB{DB: db} + }(), + }, + args: args{ + ctx: context.Background(), + unit: quota.RequestsAllAuthenticated, + instanceID: "instance_id", + periodStart: testNow, + count: 2, + }, + res: res{ + sum: 3, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q := "aProjection{ + client: tt.fields.client, + } + gotSum, err := q.IncrementUsage(tt.args.ctx, tt.args.unit, tt.args.instanceID, tt.args.periodStart, tt.args.count) + assert.Equal(t, tt.res.sum, gotSum) + assert.ErrorIs(t, err, tt.res.err) + }) + } +} diff --git a/internal/query/query.go b/internal/query/query.go index 9355265e1..216426c43 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -26,6 +26,7 @@ import ( "github.com/zitadel/zitadel/internal/repository/oidcsession" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" + "github.com/zitadel/zitadel/internal/repository/quota" "github.com/zitadel/zitadel/internal/repository/session" usr_repo "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/repository/usergrant" @@ -93,6 +94,7 @@ func StartQueries( idpintent.RegisterEventMappers(repo.eventstore) authrequest.RegisterEventMappers(repo.eventstore) oidcsession.RegisterEventMappers(repo.eventstore) + quota.RegisterEventMappers(repo.eventstore) repo.idpConfigEncryption = idpConfigEncryption repo.multifactors = domain.MultifactorConfigs{ diff --git a/internal/query/quota.go b/internal/query/quota.go new file mode 100644 index 000000000..1919902a2 --- /dev/null +++ b/internal/query/quota.go @@ -0,0 +1,121 @@ +package query + +import ( + "context" + "database/sql" + errs "errors" + "time" + + sq "github.com/Masterminds/squirrel" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/query/projection" + "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/telemetry/tracing" +) + +var ( + quotasTable = table{ + name: projection.QuotasProjectionTable, + instanceIDCol: projection.QuotaColumnInstanceID, + } + QuotaColumnID = Column{ + name: projection.QuotaColumnID, + table: quotasTable, + } + QuotaColumnInstanceID = Column{ + name: projection.QuotaColumnInstanceID, + table: quotasTable, + } + QuotaColumnUnit = Column{ + name: projection.QuotaColumnUnit, + table: quotasTable, + } + QuotaColumnAmount = Column{ + name: projection.QuotaColumnAmount, + table: quotasTable, + } + QuotaColumnLimit = Column{ + name: projection.QuotaColumnLimit, + table: quotasTable, + } + QuotaColumnInterval = Column{ + name: projection.QuotaColumnInterval, + table: quotasTable, + } + QuotaColumnFrom = Column{ + name: projection.QuotaColumnFrom, + table: quotasTable, + } +) + +type Quota struct { + ID string + From time.Time + ResetInterval time.Duration + Amount uint64 + Limit bool + CurrentPeriodStart time.Time +} + +func (q *Queries) GetQuota(ctx context.Context, instanceID string, unit quota.Unit) (qu *Quota, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + query, scan := prepareQuotaQuery(ctx, q.client) + stmt, args, err := query.Where( + sq.Eq{ + QuotaColumnInstanceID.identifier(): instanceID, + QuotaColumnUnit.identifier(): unit, + }, + ).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-XmYn9", "Errors.Query.SQLStatement") + } + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + qu, err = scan(row) + return err + }, stmt, args...) + return qu, err +} + +func prepareQuotaQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*Quota, error)) { + return sq. + Select( + QuotaColumnID.identifier(), + QuotaColumnFrom.identifier(), + QuotaColumnInterval.identifier(), + QuotaColumnAmount.identifier(), + QuotaColumnLimit.identifier(), + "now()", + ). + From(quotasTable.identifier()). + PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*Quota, error) { + q := new(Quota) + var interval database.Duration + var now time.Time + err := row.Scan(&q.ID, &q.From, &interval, &q.Amount, &q.Limit, &now) + if err != nil { + if errs.Is(err, sql.ErrNoRows) { + return nil, errors.ThrowNotFound(err, "QUERY-rDTM6", "Errors.Quota.NotExisting") + } + return nil, errors.ThrowInternal(err, "QUERY-LqySK", "Errors.Internal") + } + q.ResetInterval = time.Duration(interval) + q.CurrentPeriodStart = pushPeriodStart(q.From, q.ResetInterval, now) + return q, nil + } +} + +func pushPeriodStart(from time.Time, interval time.Duration, now time.Time) time.Time { + if now.IsZero() { + now = time.Now() + } + for { + next := from.Add(interval) + if next.After(now) { + return from + } + from = next + } +} diff --git a/internal/query/quota_model.go b/internal/query/quota_model.go deleted file mode 100644 index 322ea67aa..000000000 --- a/internal/query/quota_model.go +++ /dev/null @@ -1,55 +0,0 @@ -package query - -import ( - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/quota" -) - -type quotaReadModel struct { - eventstore.ReadModel - unit quota.Unit - active bool - config *quota.AddedEvent -} - -// newQuotaReadModel aggregateId is filled by reducing unit matching events -func newQuotaReadModel(instanceId, resourceOwner string, unit quota.Unit) *quotaReadModel { - return "aReadModel{ - ReadModel: eventstore.ReadModel{ - InstanceID: instanceId, - ResourceOwner: resourceOwner, - }, - unit: unit, - } -} - -func (rm *quotaReadModel) Query() *eventstore.SearchQueryBuilder { - query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - ResourceOwner(rm.ResourceOwner). - AllowTimeTravel(). - AddQuery(). - InstanceID(rm.InstanceID). - AggregateTypes(quota.AggregateType). - EventTypes( - quota.AddedEventType, - quota.RemovedEventType, - ).EventData(map[string]interface{}{"unit": rm.unit}) - - return query.Builder() -} - -func (rm *quotaReadModel) Reduce() error { - for _, event := range rm.Events { - switch e := event.(type) { - case *quota.AddedEvent: - rm.AggregateID = e.Aggregate().ID - rm.active = true - rm.config = e - case *quota.RemovedEvent: - rm.AggregateID = e.Aggregate().ID - rm.active = false - rm.config = nil - } - } - return rm.ReadModel.Reduce() -} diff --git a/internal/query/quota_notifications.go b/internal/query/quota_notifications.go index 63930dbe1..7fc0748f6 100644 --- a/internal/query/quota_notifications.go +++ b/internal/query/quota_notifications.go @@ -2,58 +2,180 @@ package query import ( "context" + "database/sql" + errs "errors" "math" "time" - "github.com/zitadel/zitadel/internal/eventstore" + sq "github.com/Masterminds/squirrel" + "github.com/pkg/errors" + + "github.com/zitadel/zitadel/internal/api/call" + zitadel_errors "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (q *Queries) GetDueQuotaNotifications(ctx context.Context, config *quota.AddedEvent, periodStart time.Time, usedAbs uint64) ([]*quota.NotificationDueEvent, error) { - if len(config.Notifications) == 0 { +var ( + quotaNotificationsTable = table{ + name: projection.QuotaNotificationsTable, + instanceIDCol: projection.QuotaNotificationColumnInstanceID, + } + QuotaNotificationColumnInstanceID = Column{ + name: projection.QuotaNotificationColumnInstanceID, + table: quotaNotificationsTable, + } + QuotaNotificationColumnUnit = Column{ + name: projection.QuotaNotificationColumnUnit, + table: quotaNotificationsTable, + } + QuotaNotificationColumnID = Column{ + name: projection.QuotaNotificationColumnID, + table: quotaNotificationsTable, + } + QuotaNotificationColumnCallURL = Column{ + name: projection.QuotaNotificationColumnCallURL, + table: quotaNotificationsTable, + } + QuotaNotificationColumnPercent = Column{ + name: projection.QuotaNotificationColumnPercent, + table: quotaNotificationsTable, + } + QuotaNotificationColumnRepeat = Column{ + name: projection.QuotaNotificationColumnRepeat, + table: quotaNotificationsTable, + } + QuotaNotificationColumnLatestDuePeriodStart = Column{ + name: projection.QuotaNotificationColumnLatestDuePeriodStart, + table: quotaNotificationsTable, + } + QuotaNotificationColumnNextDueThreshold = Column{ + name: projection.QuotaNotificationColumnNextDueThreshold, + table: quotaNotificationsTable, + } +) + +func (q *Queries) GetDueQuotaNotifications(ctx context.Context, instanceID string, unit quota.Unit, qu *Quota, periodStart time.Time, usedAbs uint64) (dueNotifications []*quota.NotificationDueEvent, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + usedRel := uint16(math.Floor(float64(usedAbs*100) / float64(qu.Amount))) + query, scan := prepareQuotaNotificationsQuery(ctx, q.client) + stmt, args, err := query.Where( + sq.And{ + sq.Eq{ + QuotaNotificationColumnInstanceID.identifier(): instanceID, + QuotaNotificationColumnUnit.identifier(): unit, + }, + sq.Or{ + // If the relative usage is greater than the next due threshold in the current period, it's clear we can notify + sq.And{ + sq.Eq{QuotaNotificationColumnLatestDuePeriodStart.identifier(): periodStart}, + sq.LtOrEq{QuotaNotificationColumnNextDueThreshold.identifier(): usedRel}, + }, + // In case we haven't seen a due notification for this quota period, we compare against the configured percent + sq.And{ + sq.Or{ + sq.Expr(QuotaNotificationColumnLatestDuePeriodStart.identifier() + " IS NULL"), + sq.NotEq{QuotaNotificationColumnLatestDuePeriodStart.identifier(): periodStart}, + }, + sq.LtOrEq{QuotaNotificationColumnPercent.identifier(): usedRel}, + }, + }, + }, + ).ToSql() + if err != nil { + return nil, zitadel_errors.ThrowInternal(err, "QUERY-XmYn9", "Errors.Query.SQLStatement") + } + var notifications *QuotaNotifications + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + notifications, err = scan(rows) + return err + }, stmt, args...) + if errors.Is(err, sql.ErrNoRows) { return nil, nil } - - aggregate := config.Aggregate() - wm, err := q.getQuotaNotificationsReadModel(ctx, aggregate, periodStart) if err != nil { return nil, err } - - usedRel := uint16(math.Floor(float64(usedAbs*100) / float64(config.Amount))) - - var dueNotifications []*quota.NotificationDueEvent - for _, notification := range config.Notifications { - if notification.Percent > usedRel { + for _, notification := range notifications.Configs { + reachedThreshold := calculateThreshold(usedRel, notification.Percent) + if !notification.Repeat && notification.Percent < reachedThreshold { continue } - - threshold := notification.Percent - if notification.Repeat { - threshold = uint16(math.Max(1, math.Floor(float64(usedRel)/float64(notification.Percent)))) * notification.Percent - } - - if wm.latestDueThresholds[notification.ID] < threshold { - dueNotifications = append( - dueNotifications, - quota.NewNotificationDueEvent( - ctx, - &aggregate, - config.Unit, - notification.ID, - notification.CallURL, - periodStart, - threshold, - usedAbs, - ), - ) - } + dueNotifications = append( + dueNotifications, + quota.NewNotificationDueEvent( + ctx, + "a.NewAggregate(qu.ID, instanceID).Aggregate, + unit, + notification.ID, + notification.CallURL, + periodStart, + reachedThreshold, + usedAbs, + ), + ) } - return dueNotifications, nil } -func (q *Queries) getQuotaNotificationsReadModel(ctx context.Context, aggregate eventstore.Aggregate, periodStart time.Time) (*quotaNotificationsReadModel, error) { - wm := newQuotaNotificationsReadModel(aggregate.ID, aggregate.InstanceID, aggregate.ResourceOwner, periodStart) - return wm, q.eventstore.FilterToQueryReducer(ctx, wm) +type QuotaNotification struct { + ID string + CallURL string + Percent uint16 + Repeat bool + NextDueThreshold uint16 +} + +type QuotaNotifications struct { + SearchResponse + Configs []*QuotaNotification +} + +// calculateThreshold calculates the nearest reached threshold. +// It makes sure that the percent configured on the notification is calculated within the "current" 100%, +// e.g. when configuring 80%, the thresholds are 80, 180, 280, ... +// so 170% use is always 70% of the current bucket, with the above config, the reached threshold would be 80. +func calculateThreshold(usedRel, notificationPercent uint16) uint16 { + // check how many times we reached 100% + times := math.Floor(float64(usedRel) / 100) + // check how many times we reached the percent configured with the "current" 100% + percent := math.Floor(float64(usedRel%100) / float64(notificationPercent)) + // If neither is reached, directly return 0. + // This way we don't end up in some wrong uint16 range in the calculation below. + if times == 0 && percent == 0 { + return 0 + } + return uint16(times+percent-1)*100 + notificationPercent +} + +func prepareQuotaNotificationsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*QuotaNotifications, error)) { + return sq.Select( + QuotaNotificationColumnID.identifier(), + QuotaNotificationColumnCallURL.identifier(), + QuotaNotificationColumnPercent.identifier(), + QuotaNotificationColumnRepeat.identifier(), + QuotaNotificationColumnNextDueThreshold.identifier(), + ). + From(quotaNotificationsTable.identifier() + db.Timetravel(call.Took(ctx))). + PlaceholderFormat(sq.Dollar), func(rows *sql.Rows) (*QuotaNotifications, error) { + cfgs := &QuotaNotifications{Configs: []*QuotaNotification{}} + for rows.Next() { + cfg := new(QuotaNotification) + var nextDueThreshold sql.NullInt16 + err := rows.Scan(&cfg.ID, &cfg.CallURL, &cfg.Percent, &cfg.Repeat, &nextDueThreshold) + if err != nil { + if errs.Is(err, sql.ErrNoRows) { + return nil, zitadel_errors.ThrowNotFound(err, "QUERY-bbqWb", "Errors.QuotaNotification.NotExisting") + } + return nil, zitadel_errors.ThrowInternal(err, "QUERY-8copS", "Errors.Internal") + } + if nextDueThreshold.Valid { + cfg.NextDueThreshold = uint16(nextDueThreshold.Int16) + } + cfgs.Configs = append(cfgs.Configs, cfg) + } + return cfgs, nil + } } diff --git a/internal/query/quota_notifications_model.go b/internal/query/quota_notifications_model.go deleted file mode 100644 index af809c704..000000000 --- a/internal/query/quota_notifications_model.go +++ /dev/null @@ -1,46 +0,0 @@ -package query - -import ( - "time" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/quota" -) - -type quotaNotificationsReadModel struct { - eventstore.ReadModel - periodStart time.Time - latestDueThresholds map[string]uint16 -} - -func newQuotaNotificationsReadModel(aggregateId, instanceId, resourceOwner string, periodStart time.Time) *quotaNotificationsReadModel { - return "aNotificationsReadModel{ - ReadModel: eventstore.ReadModel{ - AggregateID: aggregateId, - InstanceID: instanceId, - ResourceOwner: resourceOwner, - }, - periodStart: periodStart, - latestDueThresholds: make(map[string]uint16), - } -} - -func (rm *quotaNotificationsReadModel) Query() *eventstore.SearchQueryBuilder { - return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - ResourceOwner(rm.ResourceOwner). - AllowTimeTravel(). - AddQuery(). - InstanceID(rm.InstanceID). - AggregateTypes(quota.AggregateType). - AggregateIDs(rm.AggregateID). - CreationDateAfter(rm.periodStart). - EventTypes(quota.NotificationDueEventType).Builder() -} - -func (rm *quotaNotificationsReadModel) Reduce() error { - for _, event := range rm.Events { - e := event.(*quota.NotificationDueEvent) - rm.latestDueThresholds[e.ID] = e.Threshold - } - return rm.ReadModel.Reduce() -} diff --git a/internal/query/quota_notifications_test.go b/internal/query/quota_notifications_test.go new file mode 100644 index 000000000..a86b31df5 --- /dev/null +++ b/internal/query/quota_notifications_test.go @@ -0,0 +1,181 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_calculateThreshold(t *testing.T) { + type args struct { + usedRel uint16 + notificationPercent uint16 + } + tests := []struct { + name string + args args + want uint16 + }{ + { + name: "80 - below configuration", + args: args{ + usedRel: 70, + notificationPercent: 80, + }, + want: 0, + }, + { + name: "80 - below 100 percent use", + args: args{ + usedRel: 90, + notificationPercent: 80, + }, + want: 80, + }, + { + name: "80 - above 100 percent use", + args: args{ + usedRel: 120, + notificationPercent: 80, + }, + want: 80, + }, + { + name: "80 - more than twice the use", + args: args{ + usedRel: 190, + notificationPercent: 80, + }, + want: 180, + }, + { + name: "100 - below 100 percent use", + args: args{ + usedRel: 90, + notificationPercent: 100, + }, + want: 0, + }, + { + name: "100 - above 100 percent use", + args: args{ + usedRel: 120, + notificationPercent: 100, + }, + want: 100, + }, + { + name: "100 - more than twice the use", + args: args{ + usedRel: 210, + notificationPercent: 100, + }, + want: 200, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := calculateThreshold(tt.args.usedRel, tt.args.notificationPercent) + assert.Equal(t, int(tt.want), int(got)) + }) + } +} + +var ( + expectedQuotaNotificationsQuery = regexp.QuoteMeta(`SELECT projections.quotas_notifications.id,` + + ` projections.quotas_notifications.call_url,` + + ` projections.quotas_notifications.percent,` + + ` projections.quotas_notifications.repeat,` + + ` projections.quotas_notifications.next_due_threshold` + + ` FROM projections.quotas_notifications` + + ` AS OF SYSTEM TIME '-1 ms'`) + + quotaNotificationsCols = []string{ + "id", + "call_url", + "percent", + "repeat", + "next_due_threshold", + } +) + +func Test_prepareQuotaNotificationsQuery(t *testing.T) { + type want struct { + sqlExpectations sqlExpectation + err checkErr + } + tests := []struct { + name string + prepare interface{} + want want + object interface{} + }{ + { + name: "prepareQuotaNotificationsQuery no result", + prepare: prepareQuotaNotificationsQuery, + want: want{ + sqlExpectations: mockQueries( + expectedQuotaNotificationsQuery, + nil, + nil, + ), + }, + object: &QuotaNotifications{Configs: []*QuotaNotification{}}, + }, + { + name: "prepareQuotaNotificationsQuery", + prepare: prepareQuotaNotificationsQuery, + want: want{ + sqlExpectations: mockQuery( + expectedQuotaNotificationsQuery, + quotaNotificationsCols, + []driver.Value{ + "quota-id", + "url", + uint16(100), + true, + uint16(100), + }, + ), + }, + object: &QuotaNotifications{ + Configs: []*QuotaNotification{ + { + ID: "quota-id", + CallURL: "url", + Percent: 100, + Repeat: true, + NextDueThreshold: 100, + }, + }, + }, + }, + { + name: "prepareQuotaNotificationsQuery sql err", + prepare: prepareQuotaNotificationsQuery, + want: want{ + sqlExpectations: mockQueryErr( + expectedQuotaNotificationsQuery, + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: (*Quota)(nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err, defaultPrepareArgs...) + }) + } +} diff --git a/internal/query/quota_period.go b/internal/query/quota_period.go deleted file mode 100644 index b18a5f042..000000000 --- a/internal/query/quota_period.go +++ /dev/null @@ -1,30 +0,0 @@ -package query - -import ( - "context" - "time" - - "github.com/zitadel/zitadel/internal/repository/quota" -) - -func (q *Queries) GetCurrentQuotaPeriod(ctx context.Context, instanceID string, unit quota.Unit) (*quota.AddedEvent, time.Time, error) { - rm, err := q.getQuotaReadModel(ctx, instanceID, instanceID, unit) - if err != nil || !rm.active { - return nil, time.Time{}, err - } - - return rm.config, pushPeriodStart(rm.config.From, rm.config.ResetInterval, time.Now()), nil -} - -func pushPeriodStart(from time.Time, interval time.Duration, now time.Time) time.Time { - next := from.Add(interval) - if next.After(now) { - return from - } - return pushPeriodStart(next, interval, now) -} - -func (q *Queries) getQuotaReadModel(ctx context.Context, instanceId, resourceOwner string, unit quota.Unit) (*quotaReadModel, error) { - rm := newQuotaReadModel(instanceId, resourceOwner, unit) - return rm, q.eventstore.FilterToQueryReducer(ctx, rm) -} diff --git a/internal/query/quota_periods.go b/internal/query/quota_periods.go new file mode 100644 index 000000000..5f540e9fc --- /dev/null +++ b/internal/query/quota_periods.go @@ -0,0 +1,86 @@ +package query + +import ( + "context" + "database/sql" + "errors" + + sq "github.com/Masterminds/squirrel" + + "github.com/zitadel/zitadel/internal/api/call" + zitadel_errors "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/query/projection" + "github.com/zitadel/zitadel/internal/repository/quota" + "github.com/zitadel/zitadel/internal/telemetry/tracing" +) + +var ( + quotaPeriodsTable = table{ + name: projection.QuotaPeriodsProjectionTable, + instanceIDCol: projection.QuotaColumnInstanceID, + } + QuotaPeriodColumnInstanceID = Column{ + name: projection.QuotaPeriodColumnInstanceID, + table: quotaPeriodsTable, + } + QuotaPeriodColumnUnit = Column{ + name: projection.QuotaPeriodColumnUnit, + table: quotaPeriodsTable, + } + QuotaPeriodColumnStart = Column{ + name: projection.QuotaPeriodColumnStart, + table: quotaPeriodsTable, + } + QuotaPeriodColumnUsage = Column{ + name: projection.QuotaPeriodColumnUsage, + table: quotaPeriodsTable, + } +) + +func (q *Queries) GetRemainingQuotaUsage(ctx context.Context, instanceID string, unit quota.Unit) (remaining *uint64, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + stmt, scan := prepareRemainingQuotaUsageQuery(ctx, q.client) + query, args, err := stmt.Where( + sq.And{ + sq.Eq{ + QuotaPeriodColumnInstanceID.identifier(): instanceID, + QuotaPeriodColumnUnit.identifier(): unit, + QuotaColumnLimit.identifier(): true, + }, + sq.Expr("age(" + QuotaPeriodColumnStart.identifier() + ") < " + QuotaColumnInterval.identifier()), + sq.Expr(QuotaPeriodColumnStart.identifier() + " < now()"), + }). + ToSql() + if err != nil { + return nil, zitadel_errors.ThrowInternal(err, "QUERY-FSA3g", "Errors.Query.SQLStatement") + } + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + remaining, err = scan(row) + return err + }, query, args...) + if zitadel_errors.IsNotFound(err) { + return nil, nil + } + return remaining, err +} + +func prepareRemainingQuotaUsageQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*uint64, error)) { + return sq. + Select( + "greatest(0, " + QuotaColumnAmount.identifier() + "-" + QuotaPeriodColumnUsage.identifier() + ")", + ). + From(quotaPeriodsTable.identifier()). + Join(join(QuotaColumnUnit, QuotaPeriodColumnUnit) + db.Timetravel(call.Took(ctx))). + PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*uint64, error) { + usage := new(uint64) + err := row.Scan(usage) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, zitadel_errors.ThrowNotFound(err, "QUERY-quiowi2", "Errors.Internal") + } + return nil, zitadel_errors.ThrowInternal(err, "QUERY-81j1jn2", "Errors.Internal") + } + return usage, nil + } +} diff --git a/internal/query/quota_periods_test.go b/internal/query/quota_periods_test.go new file mode 100644 index 000000000..25ea2056e --- /dev/null +++ b/internal/query/quota_periods_test.go @@ -0,0 +1,95 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "regexp" + "testing" + + errs "github.com/zitadel/zitadel/internal/errors" +) + +var ( + expectedRemainingQuotaUsageQuery = regexp.QuoteMeta(`SELECT greatest(0, projections.quotas.amount-projections.quotas_periods.usage)` + + ` FROM projections.quotas_periods` + + ` JOIN projections.quotas ON projections.quotas_periods.unit = projections.quotas.unit AND projections.quotas_periods.instance_id = projections.quotas.instance_id` + + ` AS OF SYSTEM TIME '-1 ms'`) + remainingQuotaUsageCols = []string{ + "usage", + } +) + +func Test_prepareRemainingQuotaUsageQuery(t *testing.T) { + type want struct { + sqlExpectations sqlExpectation + err checkErr + } + tests := []struct { + name string + prepare interface{} + want want + object interface{} + }{ + { + name: "prepareRemainingQuotaUsageQuery no result", + prepare: prepareRemainingQuotaUsageQuery, + want: want{ + sqlExpectations: mockQueryScanErr( + expectedRemainingQuotaUsageQuery, + nil, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*uint64)(nil), + }, + { + name: "prepareRemainingQuotaUsageQuery", + prepare: prepareRemainingQuotaUsageQuery, + want: want{ + sqlExpectations: mockQuery( + expectedRemainingQuotaUsageQuery, + remainingQuotaUsageCols, + []driver.Value{ + uint64(100), + }, + ), + }, + object: uint64P(100), + }, + { + name: "prepareRemainingQuotaUsageQuery sql err", + prepare: prepareRemainingQuotaUsageQuery, + want: want{ + sqlExpectations: mockQueryErr( + expectedRemainingQuotaUsageQuery, + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: (*uint64)(nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err, defaultPrepareArgs...) + }) + } +} + +func uint64P(i int) *uint64 { + u := uint64(i) + return &u +} diff --git a/internal/query/quota_test.go b/internal/query/quota_test.go new file mode 100644 index 000000000..05bddc803 --- /dev/null +++ b/internal/query/quota_test.go @@ -0,0 +1,127 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "regexp" + "testing" + "time" + + "github.com/jackc/pgtype" + + errs "github.com/zitadel/zitadel/internal/errors" +) + +var ( + expectedQuotaQuery = regexp.QuoteMeta(`SELECT projections.quotas.id,` + + ` projections.quotas.from_anchor,` + + ` projections.quotas.interval,` + + ` projections.quotas.amount,` + + ` projections.quotas.limit_usage,` + + ` now()` + + ` FROM projections.quotas`) + + quotaCols = []string{ + "id", + "from_anchor", + "interval", + "amount", + "limit_usage", + "now", + } +) + +func dayNow() time.Time { + return time.Now().Truncate(24 * time.Hour) +} + +func interval(t *testing.T, src time.Duration) pgtype.Interval { + interval := pgtype.Interval{} + err := interval.Set(src) + if err != nil { + t.Fatal(err) + } + return interval +} + +func Test_QuotaPrepare(t *testing.T) { + type want struct { + sqlExpectations sqlExpectation + err checkErr + } + tests := []struct { + name string + prepare interface{} + want want + object interface{} + }{ + { + name: "prepareQuotaQuery no result", + prepare: prepareQuotaQuery, + want: want{ + sqlExpectations: mockQueriesScanErr( + expectedQuotaQuery, + nil, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*Quota)(nil), + }, + { + name: "prepareQuotaQuery", + prepare: prepareQuotaQuery, + want: want{ + sqlExpectations: mockQuery( + expectedQuotaQuery, + quotaCols, + []driver.Value{ + "quota-id", + dayNow(), + interval(t, time.Hour*24), + uint64(1000), + true, + testNow, + }, + ), + }, + object: &Quota{ + ID: "quota-id", + From: dayNow(), + ResetInterval: time.Hour * 24, + CurrentPeriodStart: dayNow(), + Amount: 1000, + Limit: true, + }, + }, + { + name: "prepareQuotaQuery sql err", + prepare: prepareQuotaQuery, + want: want{ + sqlExpectations: mockQueryErr( + expectedQuotaQuery, + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: (*Quota)(nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err, defaultPrepareArgs...) + }) + } +} diff --git a/internal/repository/quota/aggregate.go b/internal/repository/quota/aggregate.go index 0e17faed7..c1194dc34 100644 --- a/internal/repository/quota/aggregate.go +++ b/internal/repository/quota/aggregate.go @@ -13,14 +13,14 @@ type Aggregate struct { eventstore.Aggregate } -func NewAggregate(id, instanceId, resourceOwner string) *Aggregate { +func NewAggregate(id, instanceId string) *Aggregate { return &Aggregate{ Aggregate: eventstore.Aggregate{ Type: AggregateType, Version: AggregateVersion, ID: id, InstanceID: instanceId, - ResourceOwner: resourceOwner, + ResourceOwner: instanceId, }, } } diff --git a/internal/repository/quota/events.go b/internal/repository/quota/events.go index 4d635e2bd..5bdb8f0c4 100644 --- a/internal/repository/quota/events.go +++ b/internal/repository/quota/events.go @@ -14,13 +14,12 @@ import ( type Unit uint const ( - UniqueQuotaNameType = "quota_units" - UniqueQuotaNotificationIDType = "quota_notification" - eventTypePrefix = eventstore.EventType("quota.") - AddedEventType = eventTypePrefix + "added" - NotifiedEventType = eventTypePrefix + "notified" - NotificationDueEventType = eventTypePrefix + "notificationdue" - RemovedEventType = eventTypePrefix + "removed" + UniqueQuotaNameType = "quota_units" + eventTypePrefix = eventstore.EventType("quota.") + AddedEventType = eventTypePrefix + "added" + NotifiedEventType = eventTypePrefix + "notified" + NotificationDueEventType = eventTypePrefix + "notificationdue" + RemovedEventType = eventTypePrefix + "removed" ) const ( From 41e31aad41535a4778616c66b34008ade02e32d3 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Thu, 21 Sep 2023 16:45:41 +0200 Subject: [PATCH 67/76] fix: only reuse active session and use correct policies (from user org) (#6603) (cherry picked from commit 593d1605ab01d6124a6eb5b6297073e50ae8eea9) --- .../eventsourcing/eventstore/auth_request.go | 19 ++- .../eventstore/auth_request_test.go | 125 +++++++++++++++++- .../repository/eventsourcing/repository.go | 1 + .../repository/mock/repository.mock.go | 26 ++-- 4 files changed, 151 insertions(+), 20 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 301721044..950809f0c 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -50,6 +50,7 @@ type AuthRequestRepo struct { UserGrantProvider userGrantProvider ProjectProvider projectProvider ApplicationProvider applicationProvider + CustomTextProvider customTextProvider IdGenerator id.Generator } @@ -115,6 +116,10 @@ type applicationProvider interface { AppByOIDCClientID(context.Context, string, bool) (*query.App, error) } +type customTextProvider interface { + CustomTextListByTemplate(ctx context.Context, aggregateID string, text string, withOwnerRemoved bool) (texts *query.CustomTexts, err error) +} + func (repo *AuthRequestRepo) Health(ctx context.Context) error { return repo.AuthRequests.Health(ctx) } @@ -1113,8 +1118,18 @@ func (repo *AuthRequestRepo) nextStepsUser(ctx context.Context, request *domain. if len(steps) > 0 { return steps, nil } - // a single user session was found, use that automatically + // the single user session was inactive + if users[0].UserSessionState != domain.UserSessionStateActive { + return append(steps, &domain.SelectUserStep{Users: users}), nil + } + // a single active user session was found, use that automatically request.SetUserInfo(users[0].UserID, users[0].UserName, users[0].LoginName, users[0].DisplayName, users[0].AvatarKey, users[0].ResourceOwner) + if err = repo.fillPolicies(ctx, request); err != nil { + return nil, err + } + if err = repo.AuthRequests.UpdateAuthRequest(ctx, request); err != nil { + return nil, err + } } return steps, nil } @@ -1315,7 +1330,7 @@ func labelPolicyToDomain(p *query.LabelPolicy) *domain.LabelPolicy { } func (repo *AuthRequestRepo) getLoginTexts(ctx context.Context, aggregateID string) ([]*domain.CustomText, error) { - loginTexts, err := repo.Query.CustomTextListByTemplate(ctx, aggregateID, domain.LoginCustomText, false) + loginTexts, err := repo.CustomTextProvider.CustomTextListByTemplate(ctx, aggregateID, domain.LoginCustomText, false) if err != nil { return nil, err } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 7e94c61ef..7a469e472 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -6,10 +6,12 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" - "github.com/zitadel/zitadel/internal/auth_request/repository/cache" + cache "github.com/zitadel/zitadel/internal/auth_request/repository" + "github.com/zitadel/zitadel/internal/auth_request/repository/mock" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" @@ -67,6 +69,7 @@ type mockUser struct { UserID string LoginName string ResourceOwner string + SessionState domain.UserSessionState } func (m *mockViewUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) { @@ -83,9 +86,10 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string, string) ([]*user_vie sessions := make([]*user_view_model.UserSessionView, len(m.Users)) for i, user := range m.Users { sessions[i] = &user_view_model.UserSessionView{ + ResourceOwner: user.ResourceOwner, + State: int32(user.SessionState), UserID: user.UserID, LoginName: user.LoginName, - ResourceOwner: user.ResourceOwner, } } return sessions, nil @@ -148,6 +152,30 @@ func (m *mockLoginPolicy) LoginPolicyByID(ctx context.Context, _ bool, id string return m.policy, nil } +type mockPrivacyPolicy struct { + policy *query.PrivacyPolicy +} + +func (m mockPrivacyPolicy) PrivacyPolicyByOrg(ctx context.Context, b bool, s string, b2 bool) (*query.PrivacyPolicy, error) { + return m.policy, nil +} + +type mockLabelPolicy struct { + policy *query.LabelPolicy +} + +func (m mockLabelPolicy) ActiveLabelPolicyByOrg(ctx context.Context, s string, b bool) (*query.LabelPolicy, error) { + return m.policy, nil +} + +type mockCustomText struct { + texts *query.CustomTexts +} + +func (m *mockCustomText) CustomTextListByTemplate(ctx context.Context, aggregateID string, text string, withOwnerRemoved bool) (texts *query.CustomTexts, err error) { + return m.texts, nil +} + type mockLockoutPolicy struct { policy *query.LockoutPolicy } @@ -258,7 +286,7 @@ func (m *mockIDPUserLinks) IDPUserLinks(ctx context.Context, queries *query.IDPU func TestAuthRequestRepo_nextSteps(t *testing.T) { type fields struct { - AuthRequests *cache.AuthRequestCache + AuthRequests cache.AuthRequestCache View *view.View userSessionViewProvider userSessionViewProvider userViewProvider userViewProvider @@ -270,6 +298,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { loginPolicyProvider loginPolicyViewProvider lockoutPolicyProvider lockoutPolicyViewProvider idpUserLinksProvider idpUserLinksProvider + privacyPolicyProvider privacyPolicyProvider + labelPolicyProvider labelPolicyProvider + customTextProvider customTextProvider } type args struct { request *domain.AuthRequest @@ -363,11 +394,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { "id1", "loginname1", "orgID1", + domain.UserSessionStateActive, }, { "id2", "loginname2", "orgID2", + domain.UserSessionStateActive, }, }, }, @@ -402,11 +435,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { "id1", "loginname1", "orgID1", + domain.UserSessionStateActive, }, { "id2", "loginname2", "orgID2", + domain.UserSessionStateActive, }, }, }, @@ -444,6 +479,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { { "user not set single active session, callback step", fields{ + AuthRequests: func() cache.AuthRequestCache { + m := mock.NewMockAuthRequestCache(gomock.NewController(t)) + m.EXPECT().UpdateAuthRequest(gomock.Any(), gomock.Any()) + return m + }(), userSessionViewProvider: &mockViewUserSession{ PasswordVerification: time.Now().Add(-5 * time.Minute), SecondFactorVerification: time.Now().Add(-5 * time.Minute), @@ -452,6 +492,66 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { "id1", "loginname1", "orgID1", + domain.UserSessionStateActive, + }, + }, + }, + userViewProvider: &mockViewUser{ + PasswordSet: true, + IsEmailVerified: true, + MFAMaxSetUp: int32(domain.MFALevelSecondFactor), + }, + userEventProvider: &mockEventUser{}, + orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, + userGrantProvider: &mockUserGrants{}, + projectProvider: &mockProject{}, + applicationProvider: &mockApp{app: &query.App{OIDCConfig: &query.OIDCApp{AppType: domain.OIDCApplicationTypeWeb}}}, + lockoutPolicyProvider: &mockLockoutPolicy{ + policy: &query.LockoutPolicy{ + ShowFailures: true, + }, + }, + idpUserLinksProvider: &mockIDPUserLinks{}, + loginPolicyProvider: &mockLoginPolicy{ + policy: &query.LoginPolicy{ + SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP}, + PasswordCheckLifetime: 10 * 24 * time.Hour, + SecondFactorCheckLifetime: 18 * time.Hour, + }, + }, + privacyPolicyProvider: &mockPrivacyPolicy{ + policy: &query.PrivacyPolicy{}, + }, + labelPolicyProvider: &mockLabelPolicy{ + policy: &query.LabelPolicy{}, + }, + customTextProvider: &mockCustomText{ + texts: &query.CustomTexts{}, + }, + }, + args{&domain.AuthRequest{ + Request: &domain.AuthRequestOIDC{}, + LoginPolicy: &domain.LoginPolicy{ + SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeTOTP}, + PasswordCheckLifetime: 10 * 24 * time.Hour, + SecondFactorCheckLifetime: 18 * time.Hour, + }, + }, false}, + []domain.NextStep{&domain.RedirectToCallbackStep{}}, + nil, + }, + { + "user not set single inactive session, callback step", + fields{ + userSessionViewProvider: &mockViewUserSession{ + PasswordVerification: time.Now().Add(-5 * time.Minute), + SecondFactorVerification: time.Now().Add(-5 * time.Minute), + Users: []mockUser{ + { + "id1", + "loginname1", + "orgID1", + domain.UserSessionStateTerminated, }, }, }, @@ -480,11 +580,19 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { SecondFactorCheckLifetime: 18 * time.Hour, }, }, false}, - []domain.NextStep{&domain.RedirectToCallbackStep{}}, + []domain.NextStep{&domain.SelectUserStep{Users: []domain.UserSelection{ + { + UserID: "id1", + LoginName: "loginname1", + ResourceOwner: "orgID1", + UserSessionState: domain.UserSessionStateTerminated, + SelectionPossible: true, + }, + }}}, nil, }, { - "user not set multiple active sessions, select account step", + "user not set multiple sessions, select account step", fields{ userSessionViewProvider: &mockViewUserSession{ Users: []mockUser{ @@ -492,11 +600,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { "id1", "loginname1", "orgID1", + domain.UserSessionStateActive, }, { "id2", "loginname2", "orgID2", + domain.UserSessionStateTerminated, }, }, }, @@ -532,12 +642,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { LoginName: "loginname1", SelectionPossible: true, ResourceOwner: "orgID1", + UserSessionState: domain.UserSessionStateActive, }, { UserID: "id2", LoginName: "loginname2", SelectionPossible: true, ResourceOwner: "orgID2", + UserSessionState: domain.UserSessionStateTerminated, }, }, }}, @@ -1544,6 +1656,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { LoginPolicyViewProvider: tt.fields.loginPolicyProvider, LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider, IDPUserLinksProvider: tt.fields.idpUserLinksProvider, + PrivacyPolicyProvider: tt.fields.privacyPolicyProvider, + LabelPolicyProvider: tt.fields.labelPolicyProvider, + CustomTextProvider: tt.fields.customTextProvider, } got, err := repo.nextSteps(context.Background(), tt.args.request, tt.args.checkLoggedIn) if (err != nil && tt.wantErr == nil) || (tt.wantErr != nil && !tt.wantErr(err)) { diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index 454daffd1..257b49d70 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -87,6 +87,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c UserGrantProvider: queryView, ProjectProvider: queryView, ApplicationProvider: queries, + CustomTextProvider: queries, IdGenerator: idGenerator, }, eventstore.TokenRepo{ diff --git a/internal/auth_request/repository/mock/repository.mock.go b/internal/auth_request/repository/mock/repository.mock.go index 929e9aba1..74186fcf1 100644 --- a/internal/auth_request/repository/mock/repository.mock.go +++ b/internal/auth_request/repository/mock/repository.mock.go @@ -8,8 +8,8 @@ import ( context "context" reflect "reflect" - domain "github.com/zitadel/zitadel/internal/domain" gomock "github.com/golang/mock/gomock" + domain "github.com/zitadel/zitadel/internal/domain" ) // MockAuthRequestCache is a mock of AuthRequestCache interface. @@ -36,47 +36,47 @@ func (m *MockAuthRequestCache) EXPECT() *MockAuthRequestCacheMockRecorder { } // DeleteAuthRequest mocks base method. -func (m *MockAuthRequestCache) DeleteAuthRequest(arg0 context.Context, arg1, arg2 string) error { +func (m *MockAuthRequestCache) DeleteAuthRequest(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAuthRequest", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "DeleteAuthRequest", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DeleteAuthRequest indicates an expected call of DeleteAuthRequest. -func (mr *MockAuthRequestCacheMockRecorder) DeleteAuthRequest(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAuthRequestCacheMockRecorder) DeleteAuthRequest(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAuthRequest", reflect.TypeOf((*MockAuthRequestCache)(nil).DeleteAuthRequest), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAuthRequest", reflect.TypeOf((*MockAuthRequestCache)(nil).DeleteAuthRequest), arg0, arg1) } // GetAuthRequestByCode mocks base method. -func (m *MockAuthRequestCache) GetAuthRequestByCode(arg0 context.Context, arg1, arg2 string) (*domain.AuthRequest, error) { +func (m *MockAuthRequestCache) GetAuthRequestByCode(arg0 context.Context, arg1 string) (*domain.AuthRequest, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAuthRequestByCode", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "GetAuthRequestByCode", arg0, arg1) ret0, _ := ret[0].(*domain.AuthRequest) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAuthRequestByCode indicates an expected call of GetAuthRequestByCode. -func (mr *MockAuthRequestCacheMockRecorder) GetAuthRequestByCode(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAuthRequestCacheMockRecorder) GetAuthRequestByCode(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRequestByCode", reflect.TypeOf((*MockAuthRequestCache)(nil).GetAuthRequestByCode), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRequestByCode", reflect.TypeOf((*MockAuthRequestCache)(nil).GetAuthRequestByCode), arg0, arg1) } // GetAuthRequestByID mocks base method. -func (m *MockAuthRequestCache) GetAuthRequestByID(arg0 context.Context, arg1, arg2 string) (*domain.AuthRequest, error) { +func (m *MockAuthRequestCache) GetAuthRequestByID(arg0 context.Context, arg1 string) (*domain.AuthRequest, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAuthRequestByID", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "GetAuthRequestByID", arg0, arg1) ret0, _ := ret[0].(*domain.AuthRequest) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAuthRequestByID indicates an expected call of GetAuthRequestByID. -func (mr *MockAuthRequestCacheMockRecorder) GetAuthRequestByID(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAuthRequestCacheMockRecorder) GetAuthRequestByID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRequestByID", reflect.TypeOf((*MockAuthRequestCache)(nil).GetAuthRequestByID), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRequestByID", reflect.TypeOf((*MockAuthRequestCache)(nil).GetAuthRequestByID), arg0, arg1) } // Health mocks base method. From 1d4ec6cdbaf8a55ee3a58451b5354a45f81b4abe Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Fri, 22 Sep 2023 11:37:16 +0200 Subject: [PATCH 68/76] fix: set quotas (#6597) * feat: set quotas * fix: start new period on younger anchor * cleanup e2e config * fix set notifications * lint * test: fix quota projection tests * fix add quota tests * make quota fields nullable * enable amount 0 * fix initial setup * create a prerelease * avoid success comments * fix quota projection primary key * Revert "fix quota projection primary key" This reverts commit e72f4d7fa17d03d36493912168490350a320e04f. * simplify write model * fix aggregate id * avoid push without changes * test set quota lifecycle * test set quota mutations * fix quota unit test * fix: quotas * test quota.set event projection * use SetQuota in integration tests * fix: release quotas 3 * reset releaserc * fix comment * test notification order doesn't matter * test notification order doesn't matter * test with unmarshalled events * test with unmarshalled events (cherry picked from commit ae1af6bc8cd2294f47f6d6412c4b46192105ade5) --- cmd/setup/13.go | 26 + cmd/setup/13/13_fix_quota_constraints.sql | 4 + cmd/setup/config.go | 25 +- cmd/setup/setup.go | 3 + e2e/config/localhost/zitadel.yaml | 17 +- internal/api/grpc/system/quota.go | 18 +- internal/api/grpc/system/quota_converter.go | 28 +- .../api/grpc/system/quota_integration_test.go | 12 +- internal/command/instance.go | 4 +- internal/command/quota.go | 133 +++-- internal/command/quota_model.go | 153 +++++- internal/command/quota_model_test.go | 373 ++++++++++++++ internal/command/quota_test.go | 471 ++++++++++++++---- internal/eventstore/handler/crdb/statement.go | 23 +- internal/query/projection/quota.go | 99 ++-- internal/query/projection/quota_test.go | 139 +++++- internal/query/quota_periods.go | 9 +- internal/repository/quota/events.go | 97 ++-- internal/repository/quota/eventstore.go | 6 +- proto/zitadel/system.proto | 63 +++ 20 files changed, 1385 insertions(+), 318 deletions(-) create mode 100644 cmd/setup/13.go create mode 100644 cmd/setup/13/13_fix_quota_constraints.sql create mode 100644 internal/command/quota_model_test.go diff --git a/cmd/setup/13.go b/cmd/setup/13.go new file mode 100644 index 000000000..3b5df925b --- /dev/null +++ b/cmd/setup/13.go @@ -0,0 +1,26 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" +) + +var ( + //go:embed 13/13_fix_quota_constraints.sql + fixQuotaConstraints string +) + +type FixQuotaConstraints struct { + dbClient *database.DB +} + +func (mig *FixQuotaConstraints) Execute(ctx context.Context) error { + _, err := mig.dbClient.ExecContext(ctx, fixQuotaConstraints) + return err +} + +func (mig *FixQuotaConstraints) String() string { + return "13_fix_quota_constraints" +} diff --git a/cmd/setup/13/13_fix_quota_constraints.sql b/cmd/setup/13/13_fix_quota_constraints.sql new file mode 100644 index 000000000..d1f95d74a --- /dev/null +++ b/cmd/setup/13/13_fix_quota_constraints.sql @@ -0,0 +1,4 @@ +ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN from_anchor DROP NOT NULL; +ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN amount DROP NOT NULL; +ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN interval DROP NOT NULL; +ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN limit_usage DROP NOT NULL; diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 939fc9adf..3290b59e6 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -56,18 +56,19 @@ func MustNewConfig(v *viper.Viper) *Config { } type Steps struct { - s1ProjectionTable *ProjectionTable - s2AssetsTable *AssetTable - FirstInstance *FirstInstance - s4EventstoreIndexes *EventstoreIndexesNew - s5LastFailed *LastFailed - s6OwnerRemoveColumns *OwnerRemoveColumns - s7LogstoreTables *LogstoreTables - s8AuthTokens *AuthTokenIndexes - s9EventstoreIndexes2 *EventstoreIndexesNew - CorrectCreationDate *CorrectCreationDate - AddEventCreatedAt *AddEventCreatedAt - s12AddOTPColumns *AddOTPColumns + s1ProjectionTable *ProjectionTable + s2AssetsTable *AssetTable + FirstInstance *FirstInstance + s4EventstoreIndexes *EventstoreIndexesNew + s5LastFailed *LastFailed + s6OwnerRemoveColumns *OwnerRemoveColumns + s7LogstoreTables *LogstoreTables + s8AuthTokens *AuthTokenIndexes + s9EventstoreIndexes2 *EventstoreIndexesNew + CorrectCreationDate *CorrectCreationDate + AddEventCreatedAt *AddEventCreatedAt + s12AddOTPColumns *AddOTPColumns + s13FixQuotaProjection *FixQuotaConstraints } type encryptionKeyConfig struct { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 8097b6164..1efe70398 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -95,6 +95,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.AddEventCreatedAt.dbClient = dbClient steps.AddEventCreatedAt.step10 = steps.CorrectCreationDate steps.s12AddOTPColumns = &AddOTPColumns{dbClient: dbClient} + steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: dbClient} err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -137,6 +138,8 @@ func Setup(config *Config, steps *Steps, masterKey string) { logging.OnError(err).Fatal("unable to migrate step 11") err = migration.Migrate(ctx, eventstoreClient, steps.s12AddOTPColumns) logging.OnError(err).Fatal("unable to migrate step 12") + err = migration.Migrate(ctx, eventstoreClient, steps.s13FixQuotaProjection) + logging.OnError(err).Fatal("unable to migrate step 13") for _, repeatableStep := range repeatableSteps { err = migration.Migrate(ctx, eventstoreClient, repeatableStep) diff --git a/e2e/config/localhost/zitadel.yaml b/e2e/config/localhost/zitadel.yaml index de758680f..1c560ddfd 100644 --- a/e2e/config/localhost/zitadel.yaml +++ b/e2e/config/localhost/zitadel.yaml @@ -17,19 +17,6 @@ FirstInstance: Human: PasswordChangeRequired: false -LogStore: - Access: - Database: - Enabled: true - Debounce: - MinFrequency: 0s - MaxBulkSize: 0 - Execution: - Database: - Enabled: true - Stdout: - Enabled: true - Console: InstanceManagementURL: "https://example.com/instances/{{.InstanceID}}" @@ -40,6 +27,10 @@ Projections: Quotas: Access: + Enabled: true + Debounce: + MinFrequency: 0s + MaxBulkSize: 0 ExhaustedCookieKey: "zitadel.quota.limiting" ExhaustedCookieMaxAge: "600s" diff --git a/internal/api/grpc/system/quota.go b/internal/api/grpc/system/quota.go index 745c449b0..2cb57fc71 100644 --- a/internal/api/grpc/system/quota.go +++ b/internal/api/grpc/system/quota.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/api/grpc/object" "github.com/zitadel/zitadel/pkg/grpc/system" - system_pb "github.com/zitadel/zitadel/pkg/grpc/system" ) func (s *Server) AddQuota(ctx context.Context, req *system.AddQuotaRequest) (*system.AddQuotaResponse, error) { @@ -16,7 +15,20 @@ func (s *Server) AddQuota(ctx context.Context, req *system.AddQuotaRequest) (*sy if err != nil { return nil, err } - return &system_pb.AddQuotaResponse{ + return &system.AddQuotaResponse{ + Details: object.AddToDetailsPb(details.Sequence, details.EventDate, details.ResourceOwner), + }, nil +} + +func (s *Server) SetQuota(ctx context.Context, req *system.SetQuotaRequest) (*system.SetQuotaResponse, error) { + details, err := s.command.SetQuota( + ctx, + instanceQuotaPbToCommand(req), + ) + if err != nil { + return nil, err + } + return &system.SetQuotaResponse{ Details: object.AddToDetailsPb(details.Sequence, details.EventDate, details.ResourceOwner), }, nil } @@ -26,7 +38,7 @@ func (s *Server) RemoveQuota(ctx context.Context, req *system.RemoveQuotaRequest if err != nil { return nil, err } - return &system_pb.RemoveQuotaResponse{ + return &system.RemoveQuotaResponse{ Details: object.ChangeToDetailsPb(details.Sequence, details.EventDate, details.ResourceOwner), }, nil } diff --git a/internal/api/grpc/system/quota_converter.go b/internal/api/grpc/system/quota_converter.go index 5be57b43d..1945ff095 100644 --- a/internal/api/grpc/system/quota_converter.go +++ b/internal/api/grpc/system/quota_converter.go @@ -3,17 +3,27 @@ package system import ( "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/pkg/grpc/quota" - "github.com/zitadel/zitadel/pkg/grpc/system" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" ) -func instanceQuotaPbToCommand(req *system.AddQuotaRequest) *command.AddQuota { - return &command.AddQuota{ - Unit: instanceQuotaUnitPbToCommand(req.Unit), - From: req.From.AsTime(), - ResetInterval: req.ResetInterval.AsDuration(), - Amount: req.Amount, - Limit: req.Limit, - Notifications: instanceQuotaNotificationsPbToCommand(req.Notifications), +type setQuotaRequest interface { + GetUnit() quota.Unit + GetFrom() *timestamppb.Timestamp + GetResetInterval() *durationpb.Duration + GetAmount() uint64 + GetLimit() bool + GetNotifications() []*quota.Notification +} + +func instanceQuotaPbToCommand(req setQuotaRequest) *command.SetQuota { + return &command.SetQuota{ + Unit: instanceQuotaUnitPbToCommand(req.GetUnit()), + From: req.GetFrom().AsTime(), + ResetInterval: req.GetResetInterval().AsDuration(), + Amount: req.GetAmount(), + Limit: req.GetLimit(), + Notifications: instanceQuotaNotificationsPbToCommand(req.GetNotifications()), } } diff --git a/internal/api/grpc/system/quota_integration_test.go b/internal/api/grpc/system/quota_integration_test.go index a8c6840c8..ba0e2cb4d 100644 --- a/internal/api/grpc/system/quota_integration_test.go +++ b/internal/api/grpc/system/quota_integration_test.go @@ -28,7 +28,7 @@ func TestServer_QuotaNotification_Limit(t *testing.T) { percent := 50 percentAmount := amount * percent / 100 - _, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + _, err := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{ InstanceId: instanceID, Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, From: timestamppb.Now(), @@ -72,7 +72,7 @@ func TestServer_QuotaNotification_NoLimit(t *testing.T) { percent := 50 percentAmount := amount * percent / 100 - _, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + _, err := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{ InstanceId: instanceID, Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, From: timestamppb.Now(), @@ -151,7 +151,7 @@ func awaitNotification(t *testing.T, bodies chan []byte, unit quota.Unit, percen func TestServer_AddAndRemoveQuota(t *testing.T) { _, instanceID, _ := Tester.UseIsolatedInstance(CTX, SystemCTX) - got, err := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + got, err := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{ InstanceId: instanceID, Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, From: timestamppb.Now(), @@ -169,7 +169,7 @@ func TestServer_AddAndRemoveQuota(t *testing.T) { require.NoError(t, err) require.Equal(t, got.Details.ResourceOwner, instanceID) - gotAlreadyExisting, errAlreadyExisting := Tester.Client.System.AddQuota(SystemCTX, &system.AddQuotaRequest{ + gotAlreadyExisting, errAlreadyExisting := Tester.Client.System.SetQuota(SystemCTX, &system.SetQuotaRequest{ InstanceId: instanceID, Unit: quota_pb.Unit_UNIT_REQUESTS_ALL_AUTHENTICATED, From: timestamppb.Now(), @@ -184,8 +184,8 @@ func TestServer_AddAndRemoveQuota(t *testing.T) { }, }, }) - require.Error(t, errAlreadyExisting) - require.Nil(t, gotAlreadyExisting) + require.Nil(t, errAlreadyExisting) + require.Equal(t, gotAlreadyExisting.Details.ResourceOwner, instanceID) gotRemove, errRemove := Tester.Client.System.RemoveQuota(SystemCTX, &system.RemoveQuotaRequest{ InstanceId: instanceID, diff --git a/internal/command/instance.go b/internal/command/instance.go index 18c13b6d4..1677d930f 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -110,7 +110,7 @@ type InstanceSetup struct { RefreshTokenExpiration time.Duration } Quotas *struct { - Items []*AddQuota + Items []*SetQuota } } @@ -283,7 +283,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str if err != nil { return "", "", nil, nil, err } - validations = append(validations, c.AddQuotaCommand(quota.NewAggregate(quotaId, instanceID), q)) + validations = append(validations, c.SetQuotaCommand(quota.NewAggregate(quotaId, instanceID), nil, true, q)) } } diff --git a/internal/command/quota.go b/internal/command/quota.go index b78792910..442772ae5 100644 --- a/internal/command/quota.go +++ b/internal/command/quota.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/repository/quota" ) @@ -32,25 +31,25 @@ func (q QuotaUnit) Enum() quota.Unit { } } +// AddQuota returns and error if the quota already exists. +// AddQuota is deprecated. Use SetQuota instead. func (c *Commands) AddQuota( ctx context.Context, - q *AddQuota, + q *SetQuota, ) (*domain.ObjectDetails, error) { instanceId := authz.GetInstance(ctx).InstanceID() - wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, q.Unit.Enum()) if err != nil { return nil, err } - - if wm.active { + if wm.AggregateID != "" { return nil, errors.ThrowAlreadyExists(nil, "COMMAND-WDfFf", "Errors.Quota.AlreadyExists") } aggregateId, err := c.idGenerator.Next() if err != nil { return nil, err } - cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.AddQuotaCommand(quota.NewAggregate(aggregateId, instanceId), q)) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.SetQuotaCommand(quota.NewAggregate(aggregateId, instanceId), wm, true, q)) if err != nil { return nil, err } @@ -65,23 +64,52 @@ func (c *Commands) AddQuota( return writeModelToObjectDetails(&wm.WriteModel), nil } +// SetQuota creates a new quota or updates an existing one. +func (c *Commands) SetQuota( + ctx context.Context, + q *SetQuota, +) (*domain.ObjectDetails, error) { + instanceId := authz.GetInstance(ctx).InstanceID() + wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, q.Unit.Enum()) + if err != nil { + return nil, err + } + aggregateId := wm.AggregateID + createNewQuota := aggregateId == "" + if aggregateId == "" { + aggregateId, err = c.idGenerator.Next() + if err != nil { + return nil, err + } + } + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.SetQuotaCommand(quota.NewAggregate(aggregateId, instanceId), wm, createNewQuota, q)) + if err != nil { + return nil, err + } + if len(cmds) > 0 { + events, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + err = AppendAndReduce(wm, events...) + if err != nil { + return nil, err + } + } + return writeModelToObjectDetails(&wm.WriteModel), nil +} + func (c *Commands) RemoveQuota(ctx context.Context, unit QuotaUnit) (*domain.ObjectDetails, error) { instanceId := authz.GetInstance(ctx).InstanceID() - wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, unit.Enum()) if err != nil { return nil, err } - - if !wm.active { + if wm.AggregateID == "" { return nil, errors.ThrowNotFound(nil, "COMMAND-WDfFf", "Errors.Quota.NotFound") } - aggregate := quota.NewAggregate(wm.AggregateID, instanceId) - - events := []eventstore.Command{ - quota.NewRemovedEvent(ctx, &aggregate.Aggregate, unit.Enum()), - } + events := []eventstore.Command{quota.NewRemovedEvent(ctx, &aggregate.Aggregate, unit.Enum())} pushedEvents, err := c.eventstore.Push(ctx, events...) if err != nil { return nil, err @@ -104,6 +132,16 @@ type QuotaNotification struct { CallURL string } +// SetQuota configures a quota and activates it if it isn't active already +type SetQuota struct { + Unit QuotaUnit `json:"unit"` + From time.Time `json:"from"` + ResetInterval time.Duration `json:"ResetInterval,omitempty"` + Amount uint64 `json:"Amount,omitempty"` + Limit bool `json:"Limit,omitempty"` + Notifications QuotaNotifications `json:"Notifications,omitempty"` +} + type QuotaNotifications []*QuotaNotification func (q *QuotaNotification) validate() error { @@ -111,94 +149,51 @@ func (q *QuotaNotification) validate() error { if err != nil { return errors.ThrowInvalidArgument(err, "QUOTA-bZ0Fj", "Errors.Quota.Invalid.CallURL") } - if !u.IsAbs() || u.Host == "" { return errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "Errors.Quota.Invalid.CallURL") } - if q.Percent < 1 { return errors.ThrowInvalidArgument(nil, "QUOTA-pBfjq", "Errors.Quota.Invalid.Percent") } return nil } -func (q *QuotaNotifications) toAddedEventNotifications(idGenerator id.Generator) ([]*quota.AddedEventNotification, error) { - if q == nil { - return nil, nil - } - - notifications := make([]*quota.AddedEventNotification, len(*q)) - for idx, notification := range *q { - - id, err := idGenerator.Next() - if err != nil { - return nil, err - } - - notifications[idx] = "a.AddedEventNotification{ - ID: id, - Percent: notification.Percent, - Repeat: notification.Repeat, - CallURL: notification.CallURL, - } - } - - return notifications, nil -} - -type AddQuota struct { - Unit QuotaUnit - From time.Time - ResetInterval time.Duration - Amount uint64 - Limit bool - Notifications QuotaNotifications -} - -func (q *AddQuota) validate() error { +func (q *SetQuota) validate() error { for _, notification := range q.Notifications { if err := notification.validate(); err != nil { return err } } - if q.Unit.Enum() == quota.Unimplemented { return errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", "Errors.Quota.Invalid.Unimplemented") } - - if q.Amount < 1 { + if q.Amount < 0 { return errors.ThrowInvalidArgument(nil, "QUOTA-hOKSJ", "Errors.Quota.Invalid.Amount") } - if q.ResetInterval < time.Minute { return errors.ThrowInvalidArgument(nil, "QUOTA-R5otd", "Errors.Quota.Invalid.ResetInterval") } return nil } -func (c *Commands) AddQuotaCommand(a *quota.Aggregate, q *AddQuota) preparation.Validation { +func (c *Commands) SetQuotaCommand(a *quota.Aggregate, wm *quotaWriteModel, createNew bool, q *SetQuota) preparation.Validation { return func() (preparation.CreateCommands, error) { - if err := q.validate(); err != nil { return nil, err } - return func(ctx context.Context, filter preparation.FilterToQueryReducer) (cmd []eventstore.Command, err error) { - - notifications, err := q.Notifications.toAddedEventNotifications(c.idGenerator) - if err != nil { + changes, err := wm.NewChanges(c.idGenerator, createNew, q.Amount, q.From, q.ResetInterval, q.Limit, q.Notifications...) + if len(changes) == 0 { return nil, err } - - return []eventstore.Command{quota.NewAddedEvent( - ctx, - &a.Aggregate, + return []eventstore.Command{quota.NewSetEvent( + eventstore.NewBaseEventForPush( + ctx, + &a.Aggregate, + quota.SetEventType, + ), q.Unit.Enum(), - q.From, - q.ResetInterval, - q.Amount, - q.Limit, - notifications, + changes..., )}, err }, nil diff --git a/internal/command/quota_model.go b/internal/command/quota_model.go index 6e05b4df0..7336be41a 100644 --- a/internal/command/quota_model.go +++ b/internal/command/quota_model.go @@ -1,14 +1,26 @@ package command import ( + "errors" + "fmt" + "slices" + "time" + + zitadel_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/repository/quota" ) type quotaWriteModel struct { eventstore.WriteModel - unit quota.Unit - active bool + rollingAggregateID string + unit quota.Unit + from time.Time + resetInterval time.Duration + amount uint64 + limit bool + notifications []*quota.SetEventNotification } // newQuotaWriteModel aggregateId is filled by reducing unit matching events @@ -30,6 +42,7 @@ func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder { AggregateTypes(quota.AggregateType). EventTypes( quota.AddedEventType, + quota.SetEventType, quota.RemovedEventType, ).EventData(map[string]interface{}{"unit": wm.unit}) @@ -38,15 +51,137 @@ func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder { func (wm *quotaWriteModel) Reduce() error { for _, event := range wm.Events { + wm.ChangeDate = event.CreationDate() switch e := event.(type) { - case *quota.AddedEvent: - wm.AggregateID = e.Aggregate().ID - wm.ChangeDate = e.CreationDate() - wm.active = true + case *quota.SetEvent: + wm.rollingAggregateID = e.Aggregate().ID + if e.Amount != nil { + wm.amount = *e.Amount + } + if e.From != nil { + wm.from = *e.From + } + if e.Limit != nil { + wm.limit = *e.Limit + } + if e.ResetInterval != nil { + wm.resetInterval = *e.ResetInterval + } + if e.Notifications != nil { + wm.notifications = *e.Notifications + } case *quota.RemovedEvent: - wm.AggregateID = e.Aggregate().ID - wm.active = false + wm.rollingAggregateID = "" } } - return wm.WriteModel.Reduce() + if err := wm.WriteModel.Reduce(); err != nil { + return err + } + // wm.WriteModel.Reduce() sets the aggregateID to the first event's aggregateID, but we need the last one + wm.AggregateID = wm.rollingAggregateID + return nil +} + +// NewChanges returns all changes that need to be applied to the aggregate. +// If createNew is true, all quota properties are set. +func (wm *quotaWriteModel) NewChanges( + idGenerator id.Generator, + createNew bool, + amount uint64, + from time.Time, + resetInterval time.Duration, + limit bool, + notifications ...*QuotaNotification, +) (changes []quota.QuotaChange, err error) { + setEventNotifications, err := QuotaNotifications(notifications).newSetEventNotifications(idGenerator) + if err != nil { + return nil, err + } + // we sort the input notifications already, so we can return early if they have duplicates + err = sortSetEventNotifications(setEventNotifications) + if err != nil { + return nil, err + } + if createNew { + return []quota.QuotaChange{ + quota.ChangeAmount(amount), + quota.ChangeFrom(from), + quota.ChangeResetInterval(resetInterval), + quota.ChangeLimit(limit), + quota.ChangeNotifications(setEventNotifications), + }, nil + } + changes = make([]quota.QuotaChange, 0, 5) + if wm.amount != amount { + changes = append(changes, quota.ChangeAmount(amount)) + } + if wm.from != from { + changes = append(changes, quota.ChangeFrom(from)) + } + if wm.resetInterval != resetInterval { + changes = append(changes, quota.ChangeResetInterval(resetInterval)) + } + if wm.limit != limit { + changes = append(changes, quota.ChangeLimit(limit)) + } + // If the number of notifications differs, we renew the notifications and we can return early + if len(setEventNotifications) != len(wm.notifications) { + changes = append(changes, quota.ChangeNotifications(setEventNotifications)) + return changes, nil + } + // Now we sort the existing notifications too, so comparing the input properties with the existing ones is easier. + // We ignore the sorting error for the existing notifications, because this is system state, not user input. + // If sorting fails this time, the notifications are listed in the event payload and the projection cleans them up anyway. + _ = sortSetEventNotifications(wm.notifications) + for i, notification := range setEventNotifications { + if notification.CallURL != wm.notifications[i].CallURL || + notification.Percent != wm.notifications[i].Percent || + notification.Repeat != wm.notifications[i].Repeat { + changes = append(changes, quota.ChangeNotifications(setEventNotifications)) + return changes, nil + } + } + return changes, err +} + +// newSetEventNotifications returns quota.SetEventNotification elements with generated IDs. +func (q QuotaNotifications) newSetEventNotifications(idGenerator id.Generator) (setNotifications []*quota.SetEventNotification, err error) { + if q == nil { + return make([]*quota.SetEventNotification, 0), nil + } + notifications := make([]*quota.SetEventNotification, len(q)) + for idx, notification := range q { + notifications[idx] = "a.SetEventNotification{ + Percent: notification.Percent, + Repeat: notification.Repeat, + CallURL: notification.CallURL, + } + notifications[idx].ID, err = idGenerator.Next() + if err != nil { + return nil, err + } + } + return notifications, nil +} + +// sortSetEventNotifications reports an error if there are duplicate notifications or if a pointer is nil +func sortSetEventNotifications(notifications []*quota.SetEventNotification) (err error) { + slices.SortFunc(notifications, func(i, j *quota.SetEventNotification) int { + if i == nil || j == nil { + err = zitadel_errors.ThrowInternal(errors.New("sorting slices of *quota.SetEventNotification with nil pointers is not supported"), "QUOTA-8YXPk", "Errors.Internal") + return 0 + } + if i.Percent == j.Percent && i.CallURL == j.CallURL && i.Repeat == j.Repeat { + // TODO: translate + err = zitadel_errors.ThrowInternal(fmt.Errorf("%+v", i), "QUOTA-Pty2n", "Errors.Quota.Notifications.Duplicate") + return 0 + } + if i.Percent < j.Percent || + i.Percent == j.Percent && i.CallURL < j.CallURL || + i.Percent == j.Percent && i.CallURL == j.CallURL && i.Repeat == false && j.Repeat == true { + return -1 + } + return +1 + }) + return err } diff --git a/internal/command/quota_model_test.go b/internal/command/quota_model_test.go new file mode 100644 index 000000000..799c156fa --- /dev/null +++ b/internal/command/quota_model_test.go @@ -0,0 +1,373 @@ +package command + +import ( + "context" + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/id" + id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/quota" +) + +func TestQuotaWriteModel_NewChanges(t *testing.T) { + type fields struct { + from time.Time + resetInterval time.Duration + amount uint64 + limit bool + notifications []*quota.SetEventNotification + } + type args struct { + idGenerator id.Generator + createNew bool + amount uint64 + from time.Time + resetInterval time.Duration + limit bool + notifications []*QuotaNotification + } + tests := []struct { + name string + fields fields + args args + wantEvent quota.SetEvent + wantErr assert.ErrorAssertionFunc + }{{ + name: "change reset interval", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*quota.SetEventNotification, 0), + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Minute, + limit: true, + notifications: make([]*QuotaNotification, 0), + }, + wantEvent: quota.SetEvent{ + ResetInterval: durationPtr(time.Minute), + }, + wantErr: assert.NoError, + }, { + name: "change reset interval and amount", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*quota.SetEventNotification, 0), + }, + args: args{ + amount: 10, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Minute, + limit: true, + notifications: make([]*QuotaNotification, 0), + }, + wantEvent: quota.SetEvent{ + ResetInterval: durationPtr(time.Minute), + Amount: uint64Ptr(10), + }, + wantErr: assert.NoError, + }, { + name: "change nothing", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{}, + }, + wantEvent: quota.SetEvent{}, + wantErr: assert.NoError, + }, { + name: "change limit to zero value", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*quota.SetEventNotification, 0), + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: false, + notifications: make([]*QuotaNotification, 0), + }, + wantEvent: quota.SetEvent{Limit: boolPtr(false)}, + wantErr: assert.NoError, + }, { + name: "change amount to zero value", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*quota.SetEventNotification, 0), + }, + args: args{ + amount: 0, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*QuotaNotification, 0), + }, + wantEvent: quota.SetEvent{Amount: uint64Ptr(0)}, + wantErr: assert.NoError, + }, { + name: "change from to zero value", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: make([]*quota.SetEventNotification, 0), + }, + args: args{ + amount: 5, + from: time.Time{}, + resetInterval: time.Hour, + limit: true, + notifications: make([]*QuotaNotification, 0), + }, + wantEvent: quota.SetEvent{From: &time.Time{}}, + wantErr: assert.NoError, + }, { + name: "add notification", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{{ + Percent: 20, + Repeat: true, + CallURL: "https://call.url", + }}, + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "notification1"), + }, + wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{{ + ID: "notification1", + Percent: 20, + Repeat: true, + CallURL: "https://call.url", + }}}, + wantErr: assert.NoError, + }, { + name: "change nothing with notification", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{{ + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + idGenerator: id_mock.NewIDGenerator(t), + }, + wantEvent: quota.SetEvent{}, + wantErr: assert.NoError, + }, { + name: "change nothing but notification order", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }, { + ID: "notification2", + Percent: 10, + Repeat: false, + CallURL: "https://call.url", + }}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{{ + Percent: 10, + Repeat: false, + CallURL: "https://call.url", + }, { + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "newnotification1", "newnotification2"), + }, + wantEvent: quota.SetEvent{}, + wantErr: assert.NoError, + }, { + name: "change notification to zero value", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{}, + }, + wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{}}, + wantErr: assert.NoError, + }, { + name: "create new without notification", + fields: fields{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + }, + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{}, + }, + wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{}}, + wantErr: assert.NoError, + }, { + name: "create new with all values values", + args: args{ + amount: 5, + from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + resetInterval: time.Hour, + limit: true, + notifications: []*QuotaNotification{{ + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "notification1"), + createNew: true, + }, + wantEvent: quota.SetEvent{ + From: timePtr(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), + ResetInterval: durationPtr(time.Hour), + Amount: uint64Ptr(5), + Limit: boolPtr(true), + Notifications: &[]*quota.SetEventNotification{{ + ID: "notification1", + Percent: 10, + Repeat: true, + CallURL: "https://call.url", + }}, + }, + wantErr: assert.NoError, + }, { + name: "create new with zero values", + args: args{createNew: true}, + wantEvent: quota.SetEvent{ + From: &time.Time{}, + ResetInterval: durationPtr(0), + Amount: uint64Ptr(0), + Limit: boolPtr(false), + Notifications: &[]*quota.SetEventNotification{}, + }, + wantErr: assert.NoError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + wm := "aWriteModel{ + from: tt.fields.from, + resetInterval: tt.fields.resetInterval, + amount: tt.fields.amount, + limit: tt.fields.limit, + notifications: tt.fields.notifications, + } + gotChanges, err := wm.NewChanges(tt.args.idGenerator, tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications...) + if !tt.wantErr(t, err, fmt.Sprintf("NewChanges(%v, %v, %v, %v, %v, %v)", tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications)) { + return + } + marshalled, err := json.Marshal(quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "instance1").Aggregate, + quota.SetEventType, + ), + quota.Unimplemented, + gotChanges..., + )) + assert.NoError(t, err) + unmarshalled := new(quota.SetEvent) + assert.NoError(t, json.Unmarshal(marshalled, unmarshalled)) + assert.Equalf(t, tt.wantEvent, *unmarshalled, "NewChanges(%v, %v, %v, %v, %v, %v)", tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications) + }) + } +} + +func uint64Ptr(i uint64) *uint64 { return &i } +func boolPtr(b bool) *bool { return &b } +func durationPtr(d time.Duration) *time.Duration { return &d } +func timePtr(t time.Time) *time.Time { return &t } diff --git a/internal/command/quota_test.go b/internal/command/quota_test.go index a3f42b591..75c8e6bdb 100644 --- a/internal/command/quota_test.go +++ b/internal/command/quota_test.go @@ -25,7 +25,7 @@ func TestQuota_AddQuota(t *testing.T) { } type args struct { ctx context.Context - addQuota *AddQuota + setQuota *SetQuota } type res struct { want *domain.ObjectDetails @@ -44,14 +44,18 @@ func TestQuota_AddQuota(t *testing.T) { t, expectFilter( eventFromEventPusher( - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Now(), - 30*24*time.Hour, - 1000, - false, - nil, + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(false), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), ), ), ), @@ -59,13 +63,12 @@ func TestQuota_AddQuota(t *testing.T) { }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - addQuota: &AddQuota{ + setQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, - From: time.Time{}, - ResetInterval: 0, - Amount: 0, - Limit: false, - Notifications: nil, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, }, }, res: res{ @@ -83,7 +86,7 @@ func TestQuota_AddQuota(t *testing.T) { }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - addQuota: &AddQuota{ + setQuota: &SetQuota{ Unit: "unimplemented", From: time.Time{}, ResetInterval: 0, @@ -108,25 +111,28 @@ func TestQuota_AddQuota(t *testing.T) { []*repository.Event{ eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 30*24*time.Hour, - 1000, - true, - nil, + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), ), ), }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - addQuota: &AddQuota{ + setQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), ResetInterval: 30 * 24 * time.Hour, @@ -142,21 +148,25 @@ func TestQuota_AddQuota(t *testing.T) { }, }, { - name: "removed, ok", + name: "recreate quota, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Now(), - 30*24*time.Hour, - 1000, - true, - nil, + quota.ChangeFrom(time.Now()), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), ), ), eventFromEventPusherWithInstanceID( @@ -171,25 +181,28 @@ func TestQuota_AddQuota(t *testing.T) { []*repository.Event{ eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota2", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 30*24*time.Hour, - 1000, - true, - nil, + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), ), ), }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), ), ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - addQuota: &AddQuota{ + setQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), ResetInterval: 30 * 24 * time.Hour, @@ -214,32 +227,35 @@ func TestQuota_AddQuota(t *testing.T) { []*repository.Event{ eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 30*24*time.Hour, - 1000, - true, - []*quota.AddedEventNotification{ - { + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications( + []*quota.SetEventNotification{{ ID: "notification1", Percent: 20, Repeat: false, CallURL: "https://url.com", - }, - }, + }}, + ), ), ), }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - addQuota: &AddQuota{ + setQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), ResetInterval: 30 * 24 * time.Hour, @@ -267,7 +283,288 @@ func TestQuota_AddQuota(t *testing.T) { eventstore: tt.fields.eventstore, idGenerator: tt.fields.idGenerator, } - got, err := r.AddQuota(tt.args.ctx, tt.args.addQuota) + got, err := r.AddQuota(tt.args.ctx, tt.args.setQuota) + if tt.res.err == nil { + assert.NoError(t, err) + } + if tt.res.err != nil && !tt.res.err(err) { + t.Errorf("got wrong err: %v ", err) + } + if tt.res.err == nil { + assert.Equal(t, tt.res.want, got) + } + }) + } +} + +func TestQuota_SetQuota(t *testing.T) { + type fields struct { + eventstore *eventstore.Eventstore + idGenerator id.Generator + } + type args struct { + ctx context.Context + setQuota *SetQuota + } + type res struct { + want *domain.ObjectDetails + err func(error) bool + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + name: "already existing", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), + ), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + setQuota: &SetQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + { + name: "create quota, validation fail", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + setQuota: &SetQuota{ + Unit: "unimplemented", + From: time.Time{}, + ResetInterval: 0, + Amount: 0, + Limit: false, + Notifications: nil, + }, + }, + res: res{ + err: func(err error) bool { + return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", "")) + }, + }, + }, + { + name: "create quota, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), + ), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + setQuota: &SetQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + { + name: "recreate quota, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Now()), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), + ), + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewRemovedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota2", "INSTANCE").Aggregate, + quota.SetEventType, + ), + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), + ), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + setQuota: &SetQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: nil, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + { + name: "create quota with notifications, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter(), + expectPush( + []*repository.Event{ + eventFromEventPusherWithInstanceID( + "INSTANCE", + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications( + []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }}, + ), + ), + ), + }, + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + setQuota: &SetQuota{ + Unit: QuotaRequestsAllAuthenticated, + From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + ResetInterval: 30 * 24 * time.Hour, + Amount: 1000, + Limit: true, + Notifications: QuotaNotifications{ + { + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }, + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Commands{ + eventstore: tt.fields.eventstore, + idGenerator: tt.fields.idGenerator, + } + got, err := r.SetQuota(tt.args.ctx, tt.args.setQuota) if tt.res.err == nil { assert.NoError(t, err) } @@ -325,14 +622,17 @@ func TestQuota_RemoveQuota(t *testing.T) { expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Now(), - 30*24*time.Hour, - 1000, - true, - nil, + quota.ChangeFrom(time.Now()), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), ), ), eventFromEventPusherWithInstanceID( @@ -363,14 +663,17 @@ func TestQuota_RemoveQuota(t *testing.T) { expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - quota.NewAddedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, + ), QuotaRequestsAllAuthenticated.Enum(), - time.Now(), - 30*24*time.Hour, - 1000, - false, - nil, + quota.ChangeFrom(time.Now()), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(false), ), ), ), @@ -517,9 +820,9 @@ func TestQuota_QuotaNotification_validate(t *testing.T) { } } -func TestQuota_AddQuota_validate(t *testing.T) { +func TestQuota_SetQuota_validate(t *testing.T) { type args struct { - addQuota *AddQuota + addQuota *SetQuota } type res struct { err func(error) bool @@ -532,7 +835,7 @@ func TestQuota_AddQuota_validate(t *testing.T) { { name: "notification url parse failed", args: args{ - addQuota: &AddQuota{ + addQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Now(), ResetInterval: time.Minute * 10, @@ -556,7 +859,7 @@ func TestQuota_AddQuota_validate(t *testing.T) { { name: "unit unimplemented", args: args{ - addQuota: &AddQuota{ + addQuota: &SetQuota{ Unit: "unimplemented", From: time.Now(), ResetInterval: time.Minute * 10, @@ -571,28 +874,10 @@ func TestQuota_AddQuota_validate(t *testing.T) { }, }, }, - { - name: "amount 0", - args: args{ - addQuota: &AddQuota{ - Unit: QuotaRequestsAllAuthenticated, - From: time.Now(), - ResetInterval: time.Minute * 10, - Amount: 0, - Limit: true, - Notifications: nil, - }, - }, - res: res{ - err: func(err error) bool { - return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-hOKSJ", "")) - }, - }, - }, { name: "reset interval under 1 min", args: args{ - addQuota: &AddQuota{ + addQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Now(), ResetInterval: time.Second * 10, @@ -610,7 +895,7 @@ func TestQuota_AddQuota_validate(t *testing.T) { { name: "validate, ok", args: args{ - addQuota: &AddQuota{ + addQuota: &SetQuota{ Unit: QuotaRequestsAllAuthenticated, From: time.Now(), ResetInterval: time.Minute * 10, diff --git a/internal/eventstore/handler/crdb/statement.go b/internal/eventstore/handler/crdb/statement.go index efcc4952e..bc1d50d0f 100644 --- a/internal/eventstore/handler/crdb/statement.go +++ b/internal/eventstore/handler/crdb/statement.go @@ -1,11 +1,15 @@ package crdb import ( + "database/sql" + "errors" "strconv" "strings" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/database" - caos_errs "github.com/zitadel/zitadel/internal/errors" + zitadel_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/handler" ) @@ -14,8 +18,9 @@ type execOption func(*execConfig) type execConfig struct { tableName string - args []interface{} - err error + args []interface{} + err error + ignoreNotFound bool } func WithTableSuffix(name string) func(*execConfig) { @@ -24,6 +29,12 @@ func WithTableSuffix(name string) func(*execConfig) { } } +func WithIgnoreNotFound() func(*execConfig) { + return func(o *execConfig) { + o.ignoreNotFound = true + } +} + func NewCreateStatement(event eventstore.Event, values []handler.Column, opts ...execOption) *handler.Statement { cols, params, args := columnsToQuery(values) columnNames := strings.Join(cols, ", ") @@ -436,7 +447,11 @@ func exec(config execConfig, q query, opts []execOption) Exec { } if _, err := ex.Exec(q(config), config.args...); err != nil { - return caos_errs.ThrowInternal(err, "CRDB-pKtsr", "exec failed") + if config.ignoreNotFound && errors.Is(err, sql.ErrNoRows) { + logging.WithError(err).Debugf("ignored not found: %v", err) + return nil + } + return zitadel_errors.ThrowInternal(err, "CRDB-pKtsr", "exec failed") } return nil diff --git a/internal/query/projection/quota.go b/internal/query/projection/quota.go index fd6e12266..e6de8663a 100644 --- a/internal/query/projection/quota.go +++ b/internal/query/projection/quota.go @@ -5,7 +5,7 @@ import ( "time" "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" + zitadel_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" @@ -65,10 +65,10 @@ func newQuotaProjection(ctx context.Context, config crdb.StatementHandlerConfig) crdb.NewColumn(QuotaColumnID, crdb.ColumnTypeText), crdb.NewColumn(QuotaColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(QuotaColumnUnit, crdb.ColumnTypeEnum), - crdb.NewColumn(QuotaColumnAmount, crdb.ColumnTypeInt64), - crdb.NewColumn(QuotaColumnFrom, crdb.ColumnTypeTimestamp), - crdb.NewColumn(QuotaColumnInterval, crdb.ColumnTypeInterval), - crdb.NewColumn(QuotaColumnLimit, crdb.ColumnTypeBool), + crdb.NewColumn(QuotaColumnAmount, crdb.ColumnTypeInt64, crdb.Nullable()), + crdb.NewColumn(QuotaColumnFrom, crdb.ColumnTypeTimestamp, crdb.Nullable()), + crdb.NewColumn(QuotaColumnInterval, crdb.ColumnTypeInterval, crdb.Nullable()), + crdb.NewColumn(QuotaColumnLimit, crdb.ColumnTypeBool, crdb.Nullable()), }, crdb.NewPrimaryKey(QuotaColumnInstanceID, QuotaColumnUnit), ), @@ -118,31 +118,20 @@ func (q *quotaProjection) reducers() []handler.AggregateReducer { EventRedusers: []handler.EventReducer{ { Event: quota.AddedEventType, - Reduce: q.reduceQuotaAdded, + Reduce: q.reduceQuotaSet, + }, + { + Event: quota.SetEventType, + Reduce: q.reduceQuotaSet, }, - }, - }, - { - Aggregate: quota.AggregateType, - EventRedusers: []handler.EventReducer{ { Event: quota.RemovedEventType, Reduce: q.reduceQuotaRemoved, }, - }, - }, - { - Aggregate: quota.AggregateType, - EventRedusers: []handler.EventReducer{ { Event: quota.NotificationDueEventType, Reduce: q.reduceQuotaNotificationDue, }, - }, - }, - { - Aggregate: quota.AggregateType, - EventRedusers: []handler.EventReducer{ { Event: quota.NotifiedEventType, Reduce: q.reduceQuotaNotified, @@ -156,26 +145,53 @@ func (q *quotaProjection) reduceQuotaNotified(event eventstore.Event) (*handler. return crdb.NewNoOpStatement(event), nil } -func (q *quotaProjection) reduceQuotaAdded(event eventstore.Event) (*handler.Statement, error) { - e, err := assertEvent[*quota.AddedEvent](event) +func (q *quotaProjection) reduceQuotaSet(event eventstore.Event) (*handler.Statement, error) { + e, err := assertEvent[*quota.SetEvent](event) if err != nil { return nil, err } + var statements []func(e eventstore.Event) crdb.Exec - createStatements := make([]func(e eventstore.Event) crdb.Exec, len(e.Notifications)+1) - createStatements[0] = crdb.AddCreateStatement( - []handler.Column{ - handler.NewCol(QuotaColumnID, e.Aggregate().ID), - handler.NewCol(QuotaColumnInstanceID, e.Aggregate().InstanceID), - handler.NewCol(QuotaColumnUnit, e.Unit), - handler.NewCol(QuotaColumnAmount, e.Amount), - handler.NewCol(QuotaColumnFrom, e.From), - handler.NewCol(QuotaColumnInterval, e.ResetInterval), - handler.NewCol(QuotaColumnLimit, e.Limit), - }) - for i := range e.Notifications { - notification := e.Notifications[i] - createStatements[i+1] = crdb.AddCreateStatement( + // 1. Insert or update quota if the event has not only notification changes + quotaConflictColumns := []handler.Column{ + handler.NewCol(QuotaColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCol(QuotaColumnUnit, e.Unit), + } + quotaUpdateCols := make([]handler.Column, 0, 4+1+len(quotaConflictColumns)) + if e.Limit != nil { + quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnLimit, *e.Limit)) + } + if e.Amount != nil { + quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnAmount, *e.Amount)) + } + if e.From != nil { + quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnFrom, *e.From)) + } + if e.ResetInterval != nil { + quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnInterval, *e.ResetInterval)) + } + if len(quotaUpdateCols) > 0 { + // TODO: Add the quota ID to the primary key in a migration? + quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnID, e.Aggregate().ID)) + quotaUpdateCols = append(quotaUpdateCols, quotaConflictColumns...) + statements = append(statements, crdb.AddUpsertStatement(quotaConflictColumns, quotaUpdateCols)) + } + + // 2. Delete existing notifications + if e.Notifications == nil { + return crdb.NewMultiStatement(e, statements...), nil + } + statements = append(statements, crdb.AddDeleteStatement( + []handler.Condition{ + handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCond(QuotaNotificationColumnUnit, e.Unit), + }, + crdb.WithTableSuffix(quotaNotificationsTableSuffix), + )) + notifications := *e.Notifications + for i := range notifications { + notification := notifications[i] + statements = append(statements, crdb.AddCreateStatement( []handler.Column{ handler.NewCol(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(QuotaNotificationColumnUnit, e.Unit), @@ -185,10 +201,9 @@ func (q *quotaProjection) reduceQuotaAdded(event eventstore.Event) (*handler.Sta handler.NewCol(QuotaNotificationColumnRepeat, notification.Repeat), }, crdb.WithTableSuffix(quotaNotificationsTableSuffix), - ) + )) } - - return crdb.NewMultiStatement(e, createStatements...), nil + return crdb.NewMultiStatement(e, statements...), nil } func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*handler.Statement, error) { @@ -207,6 +222,8 @@ func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*h handler.NewCond(QuotaNotificationColumnID, e.ID), }, crdb.WithTableSuffix(quotaNotificationsTableSuffix), + // The notification could have been removed in the meantime + crdb.WithIgnoreNotFound(), ), nil } @@ -279,7 +296,7 @@ func (q *quotaProjection) IncrementUsage(ctx context.Context, unit quota.Unit, i instanceID, unit, periodStart, count, ).Scan(&sum) if err != nil { - return 0, errors.ThrowInternalf(err, "PROJ-SJL3h", "incrementing usage for unit %d failed for at least one quota period", unit) + return 0, zitadel_errors.ThrowInternalf(err, "PROJ-SJL3h", "incrementing usage for unit %d failed for at least one quota period", unit) } return sum, err } diff --git a/internal/query/projection/quota_test.go b/internal/query/projection/quota_test.go index dc9184da0..8bccec08a 100644 --- a/internal/query/projection/quota_test.go +++ b/internal/query/projection/quota_test.go @@ -29,7 +29,7 @@ func TestQuotasProjection_reduces(t *testing.T) { want wantReduce }{ { - name: "reduceQuotaAdded", + name: "reduceQuotaSet with added type", args: args{ event: getEvent(testEvent( repository.EventType(quota.AddedEventType), @@ -41,9 +41,9 @@ func TestQuotasProjection_reduces(t *testing.T) { "from": "2023-01-01T00:00:00Z", "interval": 300000000000 }`), - ), quota.AddedEventMapper), + ), quota.SetEventMapper), }, - reduce: ("aProjection{}).reduceQuotaAdded, + reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ aggregateType: eventstore.AggregateType("quota"), sequence: 15, @@ -51,15 +51,15 @@ func TestQuotasProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.quotas (id, instance_id, unit, amount, from_anchor, interval, limit_usage) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedStmt: "INSERT INTO projections.quotas (limit_usage, amount, from_anchor, interval, id, instance_id, unit) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (instance_id, unit) DO UPDATE SET (limit_usage, amount, from_anchor, interval, id) = (EXCLUDED.limit_usage, EXCLUDED.amount, EXCLUDED.from_anchor, EXCLUDED.interval, EXCLUDED.id)", expectedArgs: []interface{}{ - "agg-id", - "instance-id", - quota.RequestsAllAuthenticated, + true, uint64(10), time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), time.Minute * 5, - true, + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, }, }, }, @@ -67,7 +67,7 @@ func TestQuotasProjection_reduces(t *testing.T) { }, }, { - name: "reduceQuotaAdded with notification", + name: "reduceQuotaAdded with added type and notification", args: args{ event: getEvent(testEvent( repository.EventType(quota.AddedEventType), @@ -87,9 +87,9 @@ func TestQuotasProjection_reduces(t *testing.T) { } ] }`), - ), quota.AddedEventMapper), + ), quota.SetEventMapper), }, - reduce: ("aProjection{}).reduceQuotaAdded, + reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ aggregateType: eventstore.AggregateType("quota"), sequence: 15, @@ -97,17 +97,126 @@ func TestQuotasProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.quotas (id, instance_id, unit, amount, from_anchor, interval, limit_usage) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedStmt: "INSERT INTO projections.quotas (limit_usage, amount, from_anchor, interval, id, instance_id, unit) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (instance_id, unit) DO UPDATE SET (limit_usage, amount, from_anchor, interval, id) = (EXCLUDED.limit_usage, EXCLUDED.amount, EXCLUDED.from_anchor, EXCLUDED.interval, EXCLUDED.id)", expectedArgs: []interface{}{ - "agg-id", - "instance-id", - quota.RequestsAllAuthenticated, + true, uint64(10), time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), time.Minute * 5, + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas_notifications WHERE (instance_id = $1) AND (unit = $2)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + { + expectedStmt: "INSERT INTO projections.quotas_notifications (instance_id, unit, id, call_url, percent, repeat) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + "id", + "url", + uint16(100), true, }, }, + }, + }, + }, + }, + { + name: "reduceQuotaSet with set type", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.SetEventType), + quota.AggregateType, + []byte(`{ + "unit": 1, + "amount": 10, + "limit": true, + "from": "2023-01-01T00:00:00Z", + "interval": 300000000000 + }`), + ), quota.SetEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaSet, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.quotas (limit_usage, amount, from_anchor, interval, id, instance_id, unit) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (instance_id, unit) DO UPDATE SET (limit_usage, amount, from_anchor, interval, id) = (EXCLUDED.limit_usage, EXCLUDED.amount, EXCLUDED.from_anchor, EXCLUDED.interval, EXCLUDED.id)", + expectedArgs: []interface{}{ + true, + uint64(10), + time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), + time.Minute * 5, + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + }, + }, + }, + }, + { + name: "reduceQuotaAdded with set type and notification", + args: args{ + event: getEvent(testEvent( + repository.EventType(quota.SetEventType), + quota.AggregateType, + []byte(`{ + "unit": 1, + "amount": 10, + "limit": true, + "from": "2023-01-01T00:00:00Z", + "interval": 300000000000, + "notifications": [ + { + "id": "id", + "percent": 100, + "repeat": true, + "callURL": "url" + } + ] + }`), + ), quota.SetEventMapper), + }, + reduce: ("aProjection{}).reduceQuotaSet, + want: wantReduce{ + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.quotas (limit_usage, amount, from_anchor, interval, id, instance_id, unit) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (instance_id, unit) DO UPDATE SET (limit_usage, amount, from_anchor, interval, id) = (EXCLUDED.limit_usage, EXCLUDED.amount, EXCLUDED.from_anchor, EXCLUDED.interval, EXCLUDED.id)", + expectedArgs: []interface{}{ + true, + uint64(10), + time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), + time.Minute * 5, + "agg-id", + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, + { + expectedStmt: "DELETE FROM projections.quotas_notifications WHERE (instance_id = $1) AND (unit = $2)", + expectedArgs: []interface{}{ + "instance-id", + quota.RequestsAllAuthenticated, + }, + }, { expectedStmt: "INSERT INTO projections.quotas_notifications (instance_id, unit, id, call_url, percent, repeat) VALUES ($1, $2, $3, $4, $5, $6)", expectedArgs: []interface{}{ diff --git a/internal/query/quota_periods.go b/internal/query/quota_periods.go index 5f540e9fc..ce1b76cd0 100644 --- a/internal/query/quota_periods.go +++ b/internal/query/quota_periods.go @@ -49,7 +49,8 @@ func (q *Queries) GetRemainingQuotaUsage(ctx context.Context, instanceID string, QuotaColumnLimit.identifier(): true, }, sq.Expr("age(" + QuotaPeriodColumnStart.identifier() + ") < " + QuotaColumnInterval.identifier()), - sq.Expr(QuotaPeriodColumnStart.identifier() + " < now()"), + sq.Expr(QuotaPeriodColumnStart.identifier() + " <= now()"), + sq.Expr(QuotaPeriodColumnStart.identifier() + " >= " + QuotaColumnFrom.identifier()), }). ToSql() if err != nil { @@ -73,14 +74,14 @@ func prepareRemainingQuotaUsageQuery(ctx context.Context, db prepareDatabase) (s From(quotaPeriodsTable.identifier()). Join(join(QuotaColumnUnit, QuotaPeriodColumnUnit) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*uint64, error) { - usage := new(uint64) - err := row.Scan(usage) + remaining := new(uint64) + err := row.Scan(remaining) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, zitadel_errors.ThrowNotFound(err, "QUERY-quiowi2", "Errors.Internal") } return nil, zitadel_errors.ThrowInternal(err, "QUERY-81j1jn2", "Errors.Internal") } - return usage, nil + return remaining, nil } } diff --git a/internal/repository/quota/events.go b/internal/repository/quota/events.go index 5bdb8f0c4..06f41cdd0 100644 --- a/internal/repository/quota/events.go +++ b/internal/repository/quota/events.go @@ -17,6 +17,7 @@ const ( UniqueQuotaNameType = "quota_units" eventTypePrefix = eventstore.EventType("quota.") AddedEventType = eventTypePrefix + "added" + SetEventType = eventTypePrefix + "set" NotifiedEventType = eventTypePrefix + "notified" NotificationDueEventType = eventTypePrefix + "notificationdue" RemovedEventType = eventTypePrefix + "removed" @@ -43,65 +44,87 @@ func NewRemoveQuotaNameUniqueConstraint(unit Unit) *eventstore.EventUniqueConstr ) } -type AddedEvent struct { +// SetEvent describes that a quota is added or modified and contains only changed properties +type SetEvent struct { eventstore.BaseEvent `json:"-"` - - Unit Unit `json:"unit"` - From time.Time `json:"from"` - ResetInterval time.Duration `json:"interval,omitempty"` - Amount uint64 `json:"amount"` - Limit bool `json:"limit"` - Notifications []*AddedEventNotification `json:"notifications,omitempty"` + Unit Unit `json:"unit"` + From *time.Time `json:"from,omitempty"` + ResetInterval *time.Duration `json:"interval,omitempty"` + Amount *uint64 `json:"amount,omitempty"` + Limit *bool `json:"limit,omitempty"` + Notifications *[]*SetEventNotification `json:"notifications,omitempty"` } -type AddedEventNotification struct { +type SetEventNotification struct { ID string `json:"id"` Percent uint16 `json:"percent"` - Repeat bool `json:"repeat,omitempty"` + Repeat bool `json:"repeat"` CallURL string `json:"callUrl"` } -func (e *AddedEvent) Data() interface{} { +func (e *SetEvent) Data() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddQuotaUnitUniqueConstraint(e.Unit)} +func (e *SetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil } -func NewAddedEvent( - ctx context.Context, - aggregate *eventstore.Aggregate, +func NewSetEvent( + base *eventstore.BaseEvent, unit Unit, - from time.Time, - resetInterval time.Duration, - amount uint64, - limit bool, - notifications []*AddedEventNotification, -) *AddedEvent { - return &AddedEvent{ - BaseEvent: *eventstore.NewBaseEventForPush( - ctx, - aggregate, - AddedEventType, - ), - Unit: unit, - From: from, - ResetInterval: resetInterval, - Amount: amount, - Limit: limit, - Notifications: notifications, + changes ...QuotaChange, +) *SetEvent { + changedEvent := &SetEvent{ + BaseEvent: *base, + Unit: unit, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent +} + +type QuotaChange func(*SetEvent) + +func ChangeAmount(amount uint64) QuotaChange { + return func(e *SetEvent) { + e.Amount = &amount } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { - e := &AddedEvent{ +func ChangeLimit(limit bool) QuotaChange { + return func(e *SetEvent) { + e.Limit = &limit + } +} + +func ChangeFrom(from time.Time) QuotaChange { + return func(event *SetEvent) { + event.From = &from + } +} + +func ChangeResetInterval(interval time.Duration) QuotaChange { + return func(event *SetEvent) { + event.ResetInterval = &interval + } +} + +func ChangeNotifications(notifications []*SetEventNotification) QuotaChange { + return func(event *SetEvent) { + event.Notifications = ¬ifications + } +} + +func SetEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &SetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } err := json.Unmarshal(event.Data, e) if err != nil { - return nil, errors.ThrowInternal(err, "QUOTA-4n8vs", "unable to unmarshal quota added") + return nil, errors.ThrowInternal(err, "QUOTA-kmIpI", "unable to unmarshal quota set") } return e, nil diff --git a/internal/repository/quota/eventstore.go b/internal/repository/quota/eventstore.go index 2c87bb065..f651e58e2 100644 --- a/internal/repository/quota/eventstore.go +++ b/internal/repository/quota/eventstore.go @@ -5,7 +5,11 @@ import ( ) func RegisterEventMappers(es *eventstore.Eventstore) { - es.RegisterFilterEventMapper(AggregateType, AddedEventType, AddedEventMapper). + // AddedEventType is not emitted anymore. + // For ease of use, old events are directly mapped to SetEvent. + // This works, because the data structures are compatible. + es.RegisterFilterEventMapper(AggregateType, AddedEventType, SetEventMapper). + RegisterFilterEventMapper(AggregateType, SetEventType, SetEventMapper). RegisterFilterEventMapper(AggregateType, RemovedEventType, RemovedEventMapper). RegisterFilterEventMapper(AggregateType, NotificationDueEventType, NotificationDueEventMapper). RegisterFilterEventMapper(AggregateType, NotifiedEventType, NotifiedEventMapper) diff --git a/proto/zitadel/system.proto b/proto/zitadel/system.proto index e2e95dc9c..fe8e3a0ab 100644 --- a/proto/zitadel/system.proto +++ b/proto/zitadel/system.proto @@ -361,6 +361,8 @@ service SystemService { } // Creates a new quota + // Returns an error if the quota already exists for the specified unit + // Deprecated: use SetQuota instead rpc AddQuota(AddQuotaRequest) returns (AddQuotaResponse) { option (google.api.http) = { post: "/instances/{instance_id}/quotas" @@ -372,6 +374,19 @@ service SystemService { }; } + // Sets quota configuration properties + // Creates a new quota if it doesn't exist for the specified unit + rpc SetQuota(SetQuotaRequest) returns (SetQuotaResponse) { + option (google.api.http) = { + put: "/instances/{instance_id}/quotas" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; + } + // Removes a quota rpc RemoveQuota(RemoveQuotaRequest) returns (RemoveQuotaResponse) { option (google.api.http) = { @@ -598,6 +613,54 @@ message AddQuotaResponse { zitadel.v1.ObjectDetails details = 1; } +message SetQuotaRequest { + string instance_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // the unit a quota should be imposed on + zitadel.quota.v1.Unit unit = 2 [ + (validate.rules).enum = {defined_only: true, not_in: [0]}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "the unit a quota should be imposed on"; + } + ]; + // the starting time from which the current quota period is calculated from. This is relevant for querying the current usage. + google.protobuf.Timestamp from = 3 [ + (validate.rules).timestamp.required = true, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"2019-04-01T08:45:00.000000Z\""; + description: "the starting time from which the current quota period is calculated from. This is relevant for querying the current usage."; + } + ]; + // the quota periods duration + google.protobuf.Duration reset_interval = 4 [ + (validate.rules).duration.required = true, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "the quota periods duration"; + } + ]; + // the quota amount of units + uint64 amount = 5 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "the quota amount of units"; + } + ]; + // whether ZITADEL should block further usage when the configured amount is used + bool limit = 6 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "whether ZITADEL should block further usage when the configured amount is used"; + } + ]; + // the handlers, ZITADEL executes when certain quota percentages are reached + repeated zitadel.quota.v1.Notification notifications = 7 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "the handlers, ZITADEL executes when certain quota percentages are reached"; + } + ]; +} + +message SetQuotaResponse { + zitadel.v1.ObjectDetails details = 1; +} + message RemoveQuotaRequest { string instance_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; zitadel.quota.v1.Unit unit = 2; From 8ca053fad3c5b831ec50f6cdc1386f3fb8b3f870 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 9 Oct 2023 12:47:43 +0300 Subject: [PATCH 69/76] fix: respect "Ignore unknown usernames" on password reset Merge pull request from GHSA-v683-rcxx-vpff (cherry picked from commit 54676eda9806634d3b249541c30f979b4b0dce21) --- internal/api/ui/login/password_reset_handler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/api/ui/login/password_reset_handler.go b/internal/api/ui/login/password_reset_handler.go index 90da71d26..c85ca97b7 100644 --- a/internal/api/ui/login/password_reset_handler.go +++ b/internal/api/ui/login/password_reset_handler.go @@ -25,6 +25,9 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) { } user, err := l.query.GetUser(setContext(r.Context(), authReq.UserOrgID), true, false, loginName) if err != nil { + if authReq.LoginPolicy.IgnoreUnknownUsernames && errors.IsNotFound(err) { + err = nil + } l.renderPasswordResetDone(w, r, authReq, err) return } From 368962423251650b5708143ecc656c0724184059 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:09:51 +0200 Subject: [PATCH 70/76] test: correct used client api call in integration test (#6685) fix: correct used client api call in integration test Co-authored-by: Livio Spring (cherry picked from commit 6ce11a416add211bcc7e1867b9267dad8e02bb50) --- internal/api/idp/idp_integration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/api/idp/idp_integration_test.go b/internal/api/idp/idp_integration_test.go index 790f1926a..c63fb6808 100644 --- a/internal/api/idp/idp_integration_test.go +++ b/internal/api/idp/idp_integration_test.go @@ -260,10 +260,10 @@ func TestServer_SAMLACS(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := Client.StartIdentityProviderFlow(tt.args.ctx, - &user.StartIdentityProviderFlowRequest{ + got, err := Client.StartIdentityProviderIntent(tt.args.ctx, + &user.StartIdentityProviderIntentRequest{ IdpId: tt.args.idpID, - Content: &user.StartIdentityProviderFlowRequest_Urls{ + Content: &user.StartIdentityProviderIntentRequest_Urls{ Urls: &user.RedirectURLs{ SuccessUrl: tt.args.successURL, FailureUrl: tt.args.failureURL, From 907a1611acb3e9d0bddeaf4f22eb7536b2bff12f Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 9 Oct 2023 18:05:25 +0300 Subject: [PATCH 71/76] fix(login): error case for unknown usernames correctly (#6689) (cherry picked from commit 8549dd7a3d73f170937f231b9afffb4a98880abe) --- .../repository/eventsourcing/eventstore/auth_request.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 763d36f45..8dbf19455 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -756,9 +756,9 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain } // the user was either not found or not active // so check if the loginname suffix matches a verified org domain - ok, err := repo.checkDomainDiscovery(ctx, request, loginName) - if err != nil || ok { - return err + ok, errDomainDiscovery := repo.checkDomainDiscovery(ctx, request, loginName) + if errDomainDiscovery != nil || ok { + return errDomainDiscovery } // let's once again check if the user was just inactive if user != nil && user.State == int32(domain.UserStateInactive) { From 426c4acbfe4a398248bd5f62a3bac5c0cd865a58 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 13 Oct 2023 17:45:38 +0300 Subject: [PATCH 72/76] fix(notification): get origin from all relevant events and fix nil pointer (#6726) --- internal/notification/handlers/origin.go | 9 ++++--- internal/notification/types/user_email.go | 2 +- internal/notification/types/user_phone.go | 3 ++- internal/repository/session/session.go | 18 ++++++++----- internal/repository/user/human_mfa_otp.go | 33 ++++++++++++++++------- internal/repository/user/human_phone.go | 19 ++++++++----- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/internal/notification/handlers/origin.go b/internal/notification/handlers/origin.go index a807edd2d..915e1fed1 100644 --- a/internal/notification/handlers/origin.go +++ b/internal/notification/handlers/origin.go @@ -2,9 +2,10 @@ package handlers import ( "context" - "fmt" "net/url" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" http_utils "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/errors" @@ -18,11 +19,13 @@ type OriginEvent interface { } func (n *NotificationQueries) Origin(ctx context.Context, e eventstore.Event) (context.Context, error) { + var origin string originEvent, ok := e.(OriginEvent) if !ok { - return ctx, errors.ThrowInternal(fmt.Errorf("event of type %T doesn't implement OriginEvent", e), "NOTIF-3m9fs", "Errors.Internal") + logging.Errorf("event of type %T doesn't implement OriginEvent", e) + } else { + origin = originEvent.TriggerOrigin() } - origin := originEvent.TriggerOrigin() if origin != "" { originURL, err := url.Parse(origin) if err != nil { diff --git a/internal/notification/types/user_email.go b/internal/notification/types/user_email.go index 7cb3498e4..152b63076 100644 --- a/internal/notification/types/user_email.go +++ b/internal/notification/types/user_email.go @@ -33,7 +33,7 @@ func generateEmail( if err != nil { return err } - if emailChannels.Len() == 0 { + if emailChannels == nil || emailChannels.Len() == 0 { return errors.ThrowPreconditionFailed(nil, "MAIL-83nof", "Errors.Notification.Channels.NotPresent") } return emailChannels.HandleMessage(message) diff --git a/internal/notification/types/user_phone.go b/internal/notification/types/user_phone.go index 50b7f8637..6eb3314cd 100644 --- a/internal/notification/types/user_phone.go +++ b/internal/notification/types/user_phone.go @@ -4,6 +4,7 @@ import ( "context" "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/notification/messages" @@ -21,7 +22,7 @@ func generateSms( number := "" smsChannels, twilioConfig, err := channels.SMS(ctx) logging.OnError(err).Error("could not create sms channel") - if smsChannels.Len() == 0 { + if smsChannels == nil || smsChannels.Len() == 0 { return errors.ThrowPreconditionFailed(nil, "PHONE-w8nfow", "Errors.Notification.Channels.NotPresent") } if err == nil { diff --git a/internal/repository/session/session.go b/internal/repository/session/session.go index c34ef5242..8f5785a52 100644 --- a/internal/repository/session/session.go +++ b/internal/repository/session/session.go @@ -309,9 +309,10 @@ func NewTOTPCheckedEvent( type OTPSMSChallengedEvent struct { eventstore.BaseEvent `json:"-"` - Code *crypto.CryptoValue `json:"code"` - Expiry time.Duration `json:"expiry"` - CodeReturned bool `json:"codeReturned,omitempty"` + Code *crypto.CryptoValue `json:"code"` + Expiry time.Duration `json:"expiry"` + CodeReturned bool `json:"codeReturned,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } func (e *OTPSMSChallengedEvent) Data() interface{} { @@ -326,6 +327,10 @@ func (e *OTPSMSChallengedEvent) SetBaseEvent(base *eventstore.BaseEvent) { e.BaseEvent = *base } +func (e *OTPSMSChallengedEvent) TriggerOrigin() string { + return e.TriggeredAtOrigin +} + func NewOTPSMSChallengedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -339,9 +344,10 @@ func NewOTPSMSChallengedEvent( aggregate, OTPSMSChallengedType, ), - Code: code, - Expiry: expiry, - CodeReturned: codeReturned, + Code: code, + Expiry: expiry, + CodeReturned: codeReturned, + TriggeredAtOrigin: http.ComposedOrigin(ctx), } } diff --git a/internal/repository/user/human_mfa_otp.go b/internal/repository/user/human_mfa_otp.go index 52d5ad7a0..d3dd2824c 100644 --- a/internal/repository/user/human_mfa_otp.go +++ b/internal/repository/user/human_mfa_otp.go @@ -5,6 +5,7 @@ import ( "encoding/json" "time" + "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/crypto" @@ -279,8 +280,9 @@ func NewHumanOTPSMSRemovedEvent( type HumanOTPSMSCodeAddedEvent struct { eventstore.BaseEvent `json:"-"` - Code *crypto.CryptoValue `json:"code,omitempty"` - Expiry time.Duration `json:"expiry,omitempty"` + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` *AuthRequestInfo } @@ -296,6 +298,10 @@ func (e *HumanOTPSMSCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) { e.BaseEvent = *event } +func (e *HumanOTPSMSCodeAddedEvent) TriggerOrigin() string { + return e.TriggeredAtOrigin +} + func NewHumanOTPSMSCodeAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -309,9 +315,10 @@ func NewHumanOTPSMSCodeAddedEvent( aggregate, HumanOTPSMSCodeAddedType, ), - Code: code, - Expiry: expiry, - AuthRequestInfo: info, + Code: code, + Expiry: expiry, + TriggeredAtOrigin: http.ComposedOrigin(ctx), + AuthRequestInfo: info, } } @@ -473,8 +480,9 @@ func NewHumanOTPEmailRemovedEvent( type HumanOTPEmailCodeAddedEvent struct { eventstore.BaseEvent `json:"-"` - Code *crypto.CryptoValue `json:"code,omitempty"` - Expiry time.Duration `json:"expiry,omitempty"` + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` *AuthRequestInfo } @@ -490,6 +498,10 @@ func (e *HumanOTPEmailCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) e.BaseEvent = *event } +func (e *HumanOTPEmailCodeAddedEvent) TriggerOrigin() string { + return e.TriggeredAtOrigin +} + func NewHumanOTPEmailCodeAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -503,9 +515,10 @@ func NewHumanOTPEmailCodeAddedEvent( aggregate, HumanOTPEmailCodeAddedType, ), - Code: code, - Expiry: expiry, - AuthRequestInfo: info, + Code: code, + Expiry: expiry, + AuthRequestInfo: info, + TriggeredAtOrigin: http.ComposedOrigin(ctx), } } diff --git a/internal/repository/user/human_phone.go b/internal/repository/user/human_phone.go index c6586c54e..135b8f899 100644 --- a/internal/repository/user/human_phone.go +++ b/internal/repository/user/human_phone.go @@ -5,6 +5,7 @@ import ( "encoding/json" "time" + "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" @@ -149,9 +150,10 @@ func HumanPhoneVerificationFailedEventMapper(event *repository.Event) (eventstor type HumanPhoneCodeAddedEvent struct { eventstore.BaseEvent `json:"-"` - Code *crypto.CryptoValue `json:"code,omitempty"` - Expiry time.Duration `json:"expiry,omitempty"` - CodeReturned bool `json:"code_returned,omitempty"` + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` + CodeReturned bool `json:"code_returned,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } func (e *HumanPhoneCodeAddedEvent) Data() interface{} { @@ -162,6 +164,10 @@ func (e *HumanPhoneCodeAddedEvent) UniqueConstraints() []*eventstore.EventUnique return nil } +func (e *HumanPhoneCodeAddedEvent) TriggerOrigin() string { + return e.TriggeredAtOrigin +} + func NewHumanPhoneCodeAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -183,9 +189,10 @@ func NewHumanPhoneCodeAddedEventV2( aggregate, HumanPhoneCodeAddedType, ), - Code: code, - Expiry: expiry, - CodeReturned: codeReturned, + Code: code, + Expiry: expiry, + CodeReturned: codeReturned, + TriggeredAtOrigin: http.ComposedOrigin(ctx), } } From c0ddaf87e6c8f705b5a8e6a69c86e62390672dfb Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Tue, 17 Oct 2023 15:01:47 +0200 Subject: [PATCH 73/76] fix: origin from proxies (#6738) * fix: origin from proxies * test multiple forwarded header values --- .../api/http/middleware/origin_interceptor.go | 85 +++--------- .../middleware/origin_interceptor_test.go | 122 ++++++++++++++++++ 2 files changed, 143 insertions(+), 64 deletions(-) create mode 100644 internal/api/http/middleware/origin_interceptor_test.go diff --git a/internal/api/http/middleware/origin_interceptor.go b/internal/api/http/middleware/origin_interceptor.go index 2cf9a644f..da03145ab 100644 --- a/internal/api/http/middleware/origin_interceptor.go +++ b/internal/api/http/middleware/origin_interceptor.go @@ -3,7 +3,6 @@ package middleware import ( "fmt" "net/http" - "net/url" "github.com/muhlemmer/httpforwarded" "github.com/zitadel/logging" @@ -24,74 +23,32 @@ func OriginHandler(next http.Handler) http.Handler { } func composeOrigin(r *http.Request) string { - if origin, err := originFromForwardedHeader(r); err != nil { - logging.OnError(err).Debug("failed to build origin from forwarded header, trying x-forwarded-* headers") - } else { - return origin + var proto, host string + fwd, fwdErr := httpforwarded.ParseFromRequest(r) + if fwdErr == nil { + proto = oldestForwardedValue(fwd, "proto") + host = oldestForwardedValue(fwd, "host") } - if origin, err := originFromXForwardedHeaders(r); err != nil { - logging.OnError(err).Debug("failed to build origin from x-forwarded-* headers, using host header") - } else { - return origin + if proto == "" { + proto = r.Header.Get("X-Forwarded-Proto") } - scheme := "https" - if r.TLS == nil { - scheme = "http" - } - return fmt.Sprintf("%s://%s", scheme, r.Host) -} - -func originFromForwardedHeader(r *http.Request) (string, error) { - fwd, err := httpforwarded.ParseFromRequest(r) - if err != nil { - return "", err - } - var fwdProto, fwdHost, fwdPort string - if fwdProto = mostRecentValue(fwd, "proto"); fwdProto == "" { - return "", fmt.Errorf("no proto in forwarded header") - } - if fwdHost = mostRecentValue(fwd, "host"); fwdHost == "" { - return "", fmt.Errorf("no host in forwarded header") - } - fwdPort, foundFwdFor := extractPort(mostRecentValue(fwd, "for")) - if !foundFwdFor { - return "", fmt.Errorf("no for in forwarded header") - } - o := fmt.Sprintf("%s://%s", fwdProto, fwdHost) - if fwdPort != "" { - o += ":" + fwdPort - } - return o, nil -} - -func originFromXForwardedHeaders(r *http.Request) (string, error) { - scheme := r.Header.Get("X-Forwarded-Proto") - if scheme == "" { - return "", fmt.Errorf("no X-Forwarded-Proto header") - } - host := r.Header.Get("X-Forwarded-Host") if host == "" { - return "", fmt.Errorf("no X-Forwarded-Host header") + host = r.Header.Get("X-Forwarded-Host") } - return fmt.Sprintf("%s://%s", scheme, host), nil + if proto == "" { + if r.TLS == nil { + proto = "http" + } else { + proto = "https" + } + } + if host == "" { + host = r.Host + } + return fmt.Sprintf("%s://%s", proto, host) } -func extractPort(raw string) (string, bool) { - if u, ok := parseURL(raw); ok { - return u.Port(), ok - } - return "", false -} - -func parseURL(raw string) (*url.URL, bool) { - if raw == "" { - return nil, false - } - u, err := url.Parse(raw) - return u, err == nil -} - -func mostRecentValue(forwarded map[string][]string, key string) string { +func oldestForwardedValue(forwarded map[string][]string, key string) string { if forwarded == nil { return "" } @@ -99,5 +56,5 @@ func mostRecentValue(forwarded map[string][]string, key string) string { if len(values) == 0 { return "" } - return values[len(values)-1] + return values[0] } diff --git a/internal/api/http/middleware/origin_interceptor_test.go b/internal/api/http/middleware/origin_interceptor_test.go new file mode 100644 index 000000000..e8e4f306b --- /dev/null +++ b/internal/api/http/middleware/origin_interceptor_test.go @@ -0,0 +1,122 @@ +package middleware + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_composeOrigin(t *testing.T) { + type args struct { + h http.Header + } + tests := []struct { + name string + args args + want string + }{{ + name: "no proxy headers", + want: "http://host.header", + }, { + name: "forwarded proto", + args: args{ + h: http.Header{ + "Forwarded": []string{"proto=https"}, + }, + }, + want: "https://host.header", + }, { + name: "forwarded host", + args: args{ + h: http.Header{ + "Forwarded": []string{"host=forwarded.host"}, + }, + }, + want: "http://forwarded.host", + }, { + name: "forwarded proto and host", + args: args{ + h: http.Header{ + "Forwarded": []string{"proto=https;host=forwarded.host"}, + }, + }, + want: "https://forwarded.host", + }, { + name: "forwarded proto and host with multiple complete entries", + args: args{ + h: http.Header{ + "Forwarded": []string{"proto=https;host=forwarded.host, proto=http;host=forwarded.host2"}, + }, + }, + want: "https://forwarded.host", + }, { + name: "forwarded proto and host with multiple incomplete entries", + args: args{ + h: http.Header{ + "Forwarded": []string{"proto=https;host=forwarded.host, proto=http"}, + }, + }, + want: "https://forwarded.host", + }, { + name: "forwarded proto and host with incomplete entries in different values", + args: args{ + h: http.Header{ + "Forwarded": []string{"proto=http", "proto=https;host=forwarded.host", "proto=http"}, + }, + }, + want: "http://forwarded.host", + }, { + name: "x-forwarded-proto", + args: args{ + h: http.Header{ + "X-Forwarded-Proto": []string{"https"}, + }, + }, + want: "https://host.header", + }, { + name: "x-forwarded-host", + args: args{ + h: http.Header{ + "X-Forwarded-Host": []string{"x-forwarded.host"}, + }, + }, + want: "http://x-forwarded.host", + }, { + name: "x-forwarded-proto and x-forwarded-host", + args: args{ + h: http.Header{ + "X-Forwarded-Proto": []string{"https"}, + "X-Forwarded-Host": []string{"x-forwarded.host"}, + }, + }, + want: "https://x-forwarded.host", + }, { + name: "forwarded host and x-forwarded-host", + args: args{ + h: http.Header{ + "Forwarded": []string{"host=forwarded.host"}, + "X-Forwarded-Host": []string{"x-forwarded.host"}, + }, + }, + want: "http://forwarded.host", + }, { + name: "forwarded host and x-forwarded-proto", + args: args{ + h: http.Header{ + "Forwarded": []string{"host=forwarded.host"}, + "X-Forwarded-Proto": []string{"https"}, + }, + }, + want: "https://forwarded.host", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, composeOrigin(&http.Request{ + Host: "host.header", + Header: tt.args.h, + }), "headers: %+v", tt.args.h) + }) + } +} From 9c069806c433956fc0f82cc037e510a60dee8111 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Wed, 18 Oct 2023 16:32:12 +0200 Subject: [PATCH 74/76] ci(lint): use ref_base instead of main --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7789175d0..61ae27667 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -52,7 +52,7 @@ jobs: - uses: bufbuild/buf-breaking-action@v1 with: - against: "https://github.com/${{ github.repository }}.git#branch=main" + against: "https://github.com/${{ github.repository }}.git#branch=${{ github.base_ref }}" console: if: ${{ github.event_name == 'pull_request' }} From b5564572bcc037381cf1055926633c4c6a79c2ef Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 19 Oct 2023 12:19:10 +0200 Subject: [PATCH 75/76] feat(eventstore): increase parallel write capabilities (#5940) This implementation increases parallel write capabilities of the eventstore. Please have a look at the technical advisories: [05](https://zitadel.com/docs/support/advisory/a10005) and [06](https://zitadel.com/docs/support/advisory/a10006). The implementation of eventstore.push is rewritten and stored events are migrated to a new table `eventstore.events2`. If you are using cockroach: make sure that the database user of ZITADEL has `VIEWACTIVITY` grant. This is used to query events. --- .gitignore | 3 +- cmd/defaults.yaml | 74 +- cmd/initialise/helper.go | 4 +- cmd/initialise/init.go | 9 +- cmd/initialise/init_test.go | 20 +- .../sql/cockroach/03_grant_user.sql | 3 +- .../sql/cockroach/08_events_table.sql | 46 +- .../sql/postgres/08_events_table.sql | 41 +- cmd/initialise/verify_database.go | 7 +- cmd/initialise/verify_grant.go | 7 +- cmd/initialise/verify_user.go | 7 +- cmd/initialise/verify_zitadel.go | 42 +- cmd/initialise/verify_zitadel_test.go | 51 + cmd/key/key.go | 2 +- cmd/setup/01_sql/adminapi.sql | 2 +- cmd/setup/01_sql/auth.sql | 2 +- cmd/setup/04.go | 23 - cmd/setup/04/cockroach/index.sql | 4 - cmd/setup/04/postgres/index.sql | 3 - cmd/setup/09.go | 23 - cmd/setup/09/cockroach/index.sql | 51 - cmd/setup/09/postgres/index.sql | 30 - cmd/setup/10.go | 21 +- cmd/setup/10/10_create_temp_table.sql | 2 +- cmd/setup/10/10_fill_table.sql | 2 + cmd/setup/10/10_update.sql | 1 - cmd/setup/10/cockroach/10_update.sql | 1 + cmd/setup/10/postgres/10_update.sql | 10 + cmd/setup/11.go | 92 - cmd/setup/11/11_add_column.sql | 6 - cmd/setup/11/11_set_column.sql | 3 - cmd/setup/11/11_update_events.sql | 21 - cmd/setup/11/cockroach/create_index.sql | 8 - cmd/setup/11/cockroach/drop_index.sql | 1 - cmd/setup/11/postgres/create_index.sql | 4 - cmd/setup/11/postgres/drop_index.sql | 1 - cmd/setup/14.go | 72 + cmd/setup/14/cockroach/01_disable_inserts.sql | 1 + .../cockroach/02_create_and_fill_events2.sql | 33 + cmd/setup/14/cockroach/03_constraints.sql | 7 + cmd/setup/14/cockroach/04_indexes.sql | 3 + cmd/setup/14/postgres/01_disable_inserts.sql | 1 + .../postgres/02_create_and_fill_events2.sql | 31 + cmd/setup/14/postgres/03_events2_pk.sql | 4 + cmd/setup/14/postgres/04_constraints.sql | 7 + cmd/setup/14/postgres/05_indexes.sql | 3 + cmd/setup/15.go | 45 + .../15/cockroach/01_new_failed_events.sql | 16 + .../15/cockroach/02_fe_from_projections.sql | 26 + .../15/cockroach/03_fe_from_adminapi.sql | 26 + cmd/setup/15/cockroach/04_fe_from_auth.sql | 26 + cmd/setup/15/cockroach/05_current_states.sql | 15 + .../15/cockroach/06_cs_from_projections.sql | 29 + .../15/cockroach/07_cs_from_adminapi.sql | 28 + cmd/setup/15/cockroach/08_cs_from_auth.sql | 28 + .../15/postgres/01_new_failed_events.sql | 16 + .../15/postgres/02_fe_from_projections.sql | 26 + cmd/setup/15/postgres/03_fe_from_adminapi.sql | 26 + cmd/setup/15/postgres/04_fe_from_auth.sql | 26 + cmd/setup/15/postgres/05_current_states.sql | 15 + .../15/postgres/06_cs_from_projections.sql | 28 + cmd/setup/15/postgres/07_cs_from_adminapi.sql | 27 + cmd/setup/15/postgres/08_cs_from_auth.sql | 27 + cmd/setup/cleanup.go | 11 +- cmd/setup/config.go | 7 +- cmd/setup/eventstore_index.go | 29 - cmd/setup/setup.go | 68 +- cmd/start/start.go | 46 +- console/src/app/services/admin.service.ts | 8 +- console/src/app/utils/onboarding.ts | 6 + e2e/config/host.docker.internal/zitadel.yaml | 3 + e2e/config/localhost/docker-compose.yaml | 2 +- e2e/config/localhost/zitadel.yaml | 3 + internal/actions/object/metadata.go | 2 +- internal/actions/object/user.go | 4 +- internal/actions/object/user_grant.go | 2 +- internal/admin/repository/administrator.go | 14 - .../eventsourcing/eventstore/administrator.go | 51 - .../eventsourcing/handler/handler.go | 79 +- .../eventsourcing/handler/styling.go | 344 +- .../repository/eventsourcing/repository.go | 33 +- .../repository/eventsourcing/spooler/lock.go | 20 - .../eventsourcing/spooler/spooler.go | 35 - .../eventsourcing/view/error_event.go | 26 - .../repository/eventsourcing/view/sequence.go | 49 - .../repository/eventsourcing/view/styling.go | 53 +- internal/admin/repository/repository.go | 8 - internal/api/authz/context.go | 8 +- internal/api/authz/permissions.go | 24 +- internal/api/authz/permissions_test.go | 2 +- internal/api/authz/retry.go | 15 - internal/api/authz/retry_test.go | 88 - internal/api/authz/token.go | 8 +- internal/api/grpc/admin/event.go | 16 +- internal/api/grpc/admin/failed_event.go | 15 +- .../api/grpc/admin/failed_event_converter.go | 36 - .../grpc/admin/failed_event_converter_test.go | 101 - internal/api/grpc/admin/iam_member.go | 2 +- internal/api/grpc/admin/iam_settings.go | 2 +- internal/api/grpc/admin/idp.go | 4 +- internal/api/grpc/admin/instance.go | 2 +- internal/api/grpc/admin/login_policy.go | 6 +- internal/api/grpc/admin/org.go | 2 +- internal/api/grpc/admin/server.go | 4 - internal/api/grpc/admin/sms.go | 2 +- internal/api/grpc/admin/view.go | 14 +- internal/api/grpc/admin/view_converter.go | 32 +- internal/api/grpc/auth/idp.go | 2 +- internal/api/grpc/auth/permission.go | 4 +- internal/api/grpc/auth/user.go | 13 +- internal/api/grpc/management/actions.go | 2 +- internal/api/grpc/management/idp.go | 4 +- internal/api/grpc/management/org.go | 9 +- internal/api/grpc/management/policy_login.go | 6 +- internal/api/grpc/management/project.go | 16 +- .../grpc/management/project_application.go | 7 +- internal/api/grpc/management/project_grant.go | 6 +- internal/api/grpc/management/user.go | 19 +- internal/api/grpc/management/user_grant.go | 2 +- .../grpc/management/user_integration_test.go | 15 +- internal/api/grpc/object/converter.go | 2 +- internal/api/grpc/object/v2/converter.go | 4 +- .../middleware/auth_interceptor_test.go | 2 +- internal/api/grpc/system/failed_event.go | 16 +- .../api/grpc/system/failed_event_converter.go | 33 - .../system/failed_event_converter_test.go | 99 - internal/api/grpc/system/instance.go | 4 +- internal/api/grpc/system/server.go | 4 - internal/api/grpc/system/view.go | 19 +- internal/api/grpc/system/view_converter.go | 31 +- .../grpc/user/v2/password_integration_test.go | 5 +- .../api/grpc/user/v2/user_integration_test.go | 2 +- internal/api/oidc/key.go | 27 +- internal/api/saml/certificate.go | 32 +- .../eventsourcing/eventstore/auth_request.go | 49 +- .../eventstore/auth_request_test.go | 52 +- .../eventsourcing/eventstore/org.go | 4 +- .../eventsourcing/eventstore/refresh_token.go | 19 +- .../eventsourcing/eventstore/token.go | 22 +- .../eventsourcing/eventstore/user.go | 11 +- .../eventsourcing/handler/handler.go | 114 +- .../eventsourcing/handler/refresh_token.go | 211 +- .../repository/eventsourcing/handler/token.go | 360 +- .../repository/eventsourcing/handler/user.go | 646 +- .../eventsourcing/handler/user_session.go | 427 +- .../repository/eventsourcing/repository.go | 33 +- .../repository/eventsourcing/spooler/lock.go | 24 - .../eventsourcing/spooler/spooler.go | 35 - .../eventsourcing/view/error_event.go | 17 - .../eventsourcing/view/refresh_token.go | 81 +- .../repository/eventsourcing/view/sequence.go | 39 - .../repository/eventsourcing/view/token.go | 97 +- .../repository/eventsourcing/view/user.go | 95 +- .../eventsourcing/view/user_session.go | 75 +- .../repository/eventsourcing/view/view.go | 15 +- internal/auth/repository/token.go | 1 - internal/authz/authz.go | 5 +- .../eventstore/token_verifier.go | 13 +- .../eventstore/user_membership.go | 9 +- .../repository/eventsourcing/repository.go | 23 +- .../eventsourcing/view/error_event.go | 17 - .../repository/eventsourcing/view/sequence.go | 20 - .../repository/eventsourcing/view/token.go | 36 +- .../repository/eventsourcing/view/view.go | 17 +- internal/authz/repository/user_membership.go | 2 +- internal/command/auth_request_test.go | 96 +- internal/command/converter.go | 2 +- internal/command/device_auth_test.go | 99 +- internal/command/idp_intent_test.go | 131 +- internal/command/idp_model.go | 2 +- internal/command/instance.go | 14 +- .../instance_custom_login_text_test.go | 5947 ++++++----------- .../instance_custom_message_text_test.go | 123 +- .../instance_debug_notification_file_model.go | 9 +- .../instance_debug_notification_file_test.go | 43 +- .../instance_debug_notification_log_model.go | 9 +- .../instance_debug_notification_log_test.go | 89 +- internal/command/instance_domain.go | 6 +- internal/command/instance_domain_test.go | 68 +- internal/command/instance_feature_test.go | 25 +- internal/command/instance_idp_config_model.go | 1 + internal/command/instance_idp_config_test.go | 110 +- .../command/instance_idp_jwt_config_test.go | 20 +- .../command/instance_idp_oidc_config_test.go | 38 +- internal/command/instance_idp_test.go | 1725 +++-- internal/command/instance_member_test.go | 57 +- internal/command/instance_oidc_settings.go | 4 +- .../command/instance_oidc_settings_test.go | 39 +- internal/command/instance_policy_domain.go | 2 +- .../command/instance_policy_domain_test.go | 60 +- .../command/instance_policy_label_test.go | 196 +- internal/command/instance_policy_login.go | 6 +- ...ce_policy_login_identity_provider_model.go | 1 + .../command/instance_policy_login_test.go | 190 +- .../instance_policy_mail_template_model.go | 1 + .../instance_policy_mail_template_test.go | 22 +- .../instance_policy_notification_test.go | 34 +- .../instance_policy_password_age_test.go | 23 +- ...nstance_policy_password_complexity_test.go | 23 +- .../instance_policy_password_lockout_test.go | 23 +- .../command/instance_policy_privacy_test.go | 57 +- internal/command/instance_policy_security.go | 2 +- .../instance_secret_generator_model.go | 1 + internal/command/instance_settings.go | 2 +- internal/command/instance_settings_test.go | 118 +- internal/command/instance_test.go | 38 +- internal/command/main_test.go | 148 +- internal/command/oidc_session_test.go | 73 +- internal/command/org.go | 4 +- internal/command/org_action_test.go | 149 +- .../command/org_custom_login_text_test.go | 5270 ++++++--------- .../command/org_custom_message_text_test.go | 241 +- internal/command/org_domain_test.go | 147 +- internal/command/org_flow_test.go | 20 +- internal/command/org_idp_config_test.go | 146 +- internal/command/org_idp_jwt_config_test.go | 21 +- internal/command/org_idp_oidc_config_test.go | 39 +- internal/command/org_idp_test.go | 1618 +++-- internal/command/org_member_test.go | 51 +- internal/command/org_metadata_test.go | 75 +- internal/command/org_policy_domain_test.go | 148 +- internal/command/org_policy_label_test.go | 201 +- internal/command/org_policy_lockout_test.go | 29 +- internal/command/org_policy_login_test.go | 359 +- .../command/org_policy_mail_template_test.go | 27 +- .../command/org_policy_notification_test.go | 39 +- .../command/org_policy_password_age_test.go | 29 +- .../org_policy_password_complexity_test.go | 29 +- internal/command/org_policy_privacy_test.go | 57 +- internal/command/org_test.go | 571 +- internal/command/preparation/command_test.go | 4 +- internal/command/project.go | 2 +- .../command/project_application_api_test.go | 106 +- .../command/project_application_oidc_test.go | 105 +- .../command/project_application_saml_test.go | 109 +- internal/command/project_application_test.go | 68 +- internal/command/project_grant_member_test.go | 58 +- internal/command/project_grant_test.go | 132 +- internal/command/project_member_test.go | 51 +- internal/command/project_role_test.go | 198 +- internal/command/project_test.go | 283 +- internal/command/quota_model.go | 4 +- internal/command/quota_report_test.go | 117 +- internal/command/quota_test.go | 217 +- internal/command/session_otp_test.go | 8 +- internal/command/session_test.go | 52 +- internal/command/sms_config_model.go | 1 + internal/command/sms_config_test.go | 88 +- internal/command/smtp.go | 8 +- internal/command/smtp_test.go | 196 +- internal/command/unique_constraints_model.go | 4 +- internal/command/user_grant.go | 2 +- internal/command/user_grant_test.go | 173 +- internal/command/user_human.go | 2 +- internal/command/user_human_adress_test.go | 21 +- internal/command/user_human_avatar_test.go | 25 +- internal/command/user_human_email_test.go | 167 +- internal/command/user_human_init_test.go | 141 +- internal/command/user_human_otp.go | 2 +- internal/command/user_human_otp_test.go | 604 +- internal/command/user_human_password.go | 2 +- internal/command/user_human_password_test.go | 220 +- internal/command/user_human_phone_test.go | 183 +- internal/command/user_human_profile_test.go | 23 +- .../command/user_human_refresh_token_test.go | 69 +- internal/command/user_human_test.go | 895 +-- internal/command/user_idp_link.go | 2 +- internal/command/user_idp_link_test.go | 86 +- internal/command/user_machine.go | 4 +- internal/command/user_machine_key.go | 4 +- internal/command/user_machine_key_test.go | 37 +- internal/command/user_machine_secret.go | 6 +- internal/command/user_machine_secret_test.go | 53 +- internal/command/user_machine_test.go | 28 +- internal/command/user_metadata_test.go | 75 +- .../command/user_personal_access_token.go | 4 +- .../user_personal_access_token_test.go | 58 +- internal/command/user_test.go | 243 +- internal/command/user_v2_email_test.go | 161 +- internal/command/user_v2_passkey_test.go | 147 +- internal/command/user_v2_password_test.go | 100 +- internal/command/user_v2_phone_test.go | 93 +- internal/command/user_v2_totp_test.go | 15 +- internal/command/user_v2_u2f_test.go | 9 +- internal/database/cockroach/config.go | 169 - internal/database/cockroach/crdb.go | 179 + internal/database/database.go | 38 +- internal/database/dialect/config.go | 7 +- internal/database/dialect/connections.go | 39 + internal/database/mock/sql_mock.go | 129 + internal/database/postgres/config.go | 172 - internal/database/postgres/pg.go | 180 +- internal/database/type.go | 37 +- internal/database/type_test.go | 82 + internal/eventstore/aggregate.go | 48 +- internal/eventstore/bench_payload.txt | 33 + internal/eventstore/config.go | 20 +- internal/eventstore/event.go | 111 +- internal/eventstore/event_base.go | 99 +- internal/eventstore/eventstore.go | 241 +- internal/eventstore/eventstore_bench_test.go | 162 + internal/eventstore/eventstore_pusher_test.go | 712 ++ .../eventstore/eventstore_querier_test.go | 182 + internal/eventstore/eventstore_test.go | 887 +-- internal/eventstore/example_test.go | 54 +- .../handler/crdb/current_sequence.go | 83 - .../eventstore/handler/crdb/db_mock_test.go | 294 +- .../eventstore/handler/crdb/failed_stmt.go | 51 - .../eventstore/handler/crdb/handler_stmt.go | 347 - .../handler/crdb/handler_stmt_test.go | 1817 ----- internal/eventstore/handler/crdb/init_test.go | 49 - internal/eventstore/handler/crdb/lock.go | 2 +- internal/eventstore/handler/crdb/lock_test.go | 6 +- internal/eventstore/handler/crdb/reduce.go | 16 - internal/eventstore/handler/handler.go | 36 - .../eventstore/handler/handler_projection.go | 396 -- .../handler/handler_projection_test.go | 1228 ---- internal/eventstore/handler/init.go | 2 +- internal/eventstore/handler/reduce.go | 17 - internal/eventstore/handler/statement.go | 69 - internal/eventstore/handler/v2/event.go | 52 + .../eventstore/handler/v2/failed_event.go | 95 + .../handler/v2/failed_event_get_count.sql | 12 + .../handler/v2/failed_event_set.sql | 31 + internal/eventstore/handler/v2/handler.go | 465 ++ .../eventstore/handler/{crdb => v2}/init.go | 81 +- internal/eventstore/handler/v2/log.go | 23 + internal/eventstore/handler/v2/mock_test.go | 18 + internal/eventstore/handler/v2/reduce.go | 21 + internal/eventstore/handler/v2/state.go | 119 + internal/eventstore/handler/v2/state_get.sql | 12 + .../eventstore/handler/v2/state_get_await.sql | 12 + internal/eventstore/handler/v2/state_lock.sql | 9 + internal/eventstore/handler/v2/state_set.sql | 29 + .../handler/v2/state_set_last_run.sql | 2 + internal/eventstore/handler/v2/state_test.go | 447 ++ .../handler/{crdb => v2}/statement.go | 367 +- .../handler/{crdb => v2}/statement_test.go | 388 +- internal/eventstore/local_crdb_test.go | 176 +- internal/eventstore/read_model.go | 4 +- internal/eventstore/repository/event.go | 98 +- .../eventstore/repository/mock/gen_mock.go | 2 +- .../repository/mock/repository.mock.go | 128 +- .../repository/mock/repository.mock.impl.go | 196 +- .../repository.mock.matcher.searchquery.go | 12 +- internal/eventstore/repository/repository.go | 23 - .../eventstore/repository/search_query.go | 204 +- .../repository/search_query_test.go | 8 +- internal/eventstore/repository/sql/crdb.go | 296 +- .../eventstore/repository/sql/crdb_test.go | 1037 +-- .../repository/sql/local_crdb_test.go | 38 +- internal/eventstore/repository/sql/query.go | 272 +- .../eventstore/repository/sql/query_test.go | 650 +- internal/eventstore/repository/sql/types.go | 49 - .../repository/unique_constraint.go | 33 - internal/eventstore/repository/version.go | 20 - internal/eventstore/search_query.go | 369 +- internal/eventstore/search_query_test.go | 715 +- internal/eventstore/subscription.go | 42 +- internal/eventstore/unique_constraint.go | 44 +- internal/eventstore/v1/eventstore.go | 51 - internal/eventstore/v1/gen_mock.go | 3 - .../v1/internal/repository/gen_mock.go | 3 - .../v1/internal/repository/mock/repository.go | 34 - .../repository/mock/repository.mock.go | 98 - .../v1/internal/repository/repository.go | 18 - .../v1/internal/repository/sql/config.go | 12 - .../internal/repository/sql/db_mock_test.go | 208 - .../v1/internal/repository/sql/filter.go | 99 - .../v1/internal/repository/sql/filter_test.go | 243 - .../v1/internal/repository/sql/query.go | 238 - .../v1/internal/repository/sql/query_test.go | 504 -- .../v1/internal/repository/sql/sql.go | 16 - .../v1/internal/repository/sql/types.go | 47 - internal/eventstore/v1/locker/lock.go | 47 - internal/eventstore/v1/locker/lock_test.go | 126 - .../eventstore/v1/mock/eventstore.mock.go | 84 - internal/eventstore/v1/models/aggregate.go | 99 - .../eventstore/v1/models/aggregate_creator.go | 59 - .../v1/models/aggregate_creator_test.go | 118 - .../eventstore/v1/models/aggregate_test.go | 310 - internal/eventstore/v1/models/event.go | 84 +- internal/eventstore/v1/models/event_test.go | 42 +- internal/eventstore/v1/models/field.go | 15 - internal/eventstore/v1/models/filter.go | 46 - internal/eventstore/v1/models/filter_test.go | 104 - internal/eventstore/v1/models/object.go | 16 +- internal/eventstore/v1/models/object_test.go | 8 +- internal/eventstore/v1/models/operation.go | 11 - internal/eventstore/v1/models/search_query.go | 296 - .../eventstore/v1/models/search_query_old.go | 148 - .../v1/models/search_query_old_test.go | 65 - .../eventstore/v1/models/search_query_test.go | 590 -- internal/eventstore/v1/models/version.go | 22 - internal/eventstore/v1/models/version_test.go | 39 - internal/eventstore/v1/query/handler.go | 93 - .../eventstore/v1/sdk/append_event_error.go | 36 - .../v1/sdk/append_event_error_test.go | 31 - internal/eventstore/v1/sdk/sdk.go | 27 - internal/eventstore/v1/sdk/sdk_test.go | 77 - internal/eventstore/v1/spooler/config.go | 42 - internal/eventstore/v1/spooler/mock.go | 3 - .../eventstore/v1/spooler/mock/spooler.go | 49 - internal/eventstore/v1/spooler/spooler.go | 292 - .../eventstore/v1/spooler/spooler_test.go | 514 -- internal/eventstore/v1/subscription.go | 74 - internal/eventstore/v3/event.go | 107 + internal/eventstore/v3/event_test.go | 128 + internal/eventstore/v3/eventstore.go | 29 + internal/eventstore/v3/mock_test.go | 64 + internal/eventstore/v3/push.go | 171 + internal/eventstore/v3/push.sql | 18 + internal/eventstore/v3/push_test.go | 253 + internal/eventstore/v3/sequence.go | 132 + internal/eventstore/v3/sequence_test.go | 293 + internal/eventstore/v3/sequences_query.sql | 30 + internal/eventstore/v3/type.go | 25 + internal/eventstore/v3/unique_constraints.go | 89 + .../eventstore/v3/unique_constraints_add.sql | 6 + .../v3/unique_constraints_delete.sql | 1 + internal/eventstore/version.go | 15 +- .../{repository => }/version_test.go | 2 +- internal/eventstore/write_model.go | 8 +- .../eventsourcing/model/org_iam_policy.go | 7 +- .../iam/repository/view/model/idp_config.go | 58 +- .../iam/repository/view/model/idp_provider.go | 22 +- .../iam/repository/view/model/label_policy.go | 25 +- .../view/model/password_complexity_policy.go | 20 +- internal/idp/providers/oauth/session.go | 6 +- internal/integration/config/postgres.yaml | 3 +- internal/integration/config/zitadel.yaml | 3 + internal/migration/command.go | 28 +- internal/migration/migration.go | 29 +- .../notification/handlers/already_handled.go | 2 +- internal/notification/handlers/ctx.go | 2 +- .../notification/handlers/quota_notifier.go | 65 +- .../notification/handlers/telemetry_pusher.go | 110 +- .../notification/handlers/user_notifier.go | 625 +- .../handlers/user_notifier_test.go | 216 +- internal/notification/projections.go | 6 +- internal/notification/senders/email.go | 1 - internal/notification/senders/webhook.go | 1 - .../repository/eventsourcing/model/domain.go | 19 +- .../eventsourcing/model/domain_policy.go | 8 +- .../org/repository/eventsourcing/model/org.go | 14 +- .../eventsourcing/model/org_test.go | 21 +- internal/org/repository/view/query.go | 43 +- .../eventsourcing/model/oidc_config.go | 3 +- .../repository/eventsourcing/model/project.go | 24 +- .../eventsourcing/model/project_test.go | 21 +- .../repository/view/model/application.go | 47 +- .../repository/view/model/application_test.go | 12 +- .../project/repository/view/model/project.go | 11 +- .../repository/view/model/project_grant.go | 29 +- .../view/model/project_grant_member.go | 31 +- .../view/model/project_grant_member_test.go | 4 +- .../view/model/project_grant_test.go | 8 +- .../repository/view/model/project_member.go | 29 +- .../view/model/project_member_test.go | 4 +- .../repository/view/model/project_test.go | 8 +- internal/project/repository/view/query.go | 33 +- internal/query/access_token.go | 6 +- internal/query/action.go | 2 +- internal/query/app.go | 38 +- internal/query/app_test.go | 272 +- internal/query/auth_request.go | 12 +- internal/query/auth_request_test.go | 18 +- internal/query/authn_key.go | 10 +- internal/query/certificate.go | 2 +- internal/query/current_sequence.go | 320 - internal/query/current_state.go | 378 ++ ...sequence_test.go => current_state_test.go} | 100 +- internal/query/custom_text.go | 4 +- internal/query/device_auth.go | 2 +- internal/query/device_auth_test.go | 2 +- internal/query/domain_policy.go | 6 +- internal/query/event.go | 16 +- internal/query/failed_events.go | 16 + internal/query/failed_events_test.go | 29 +- internal/query/iam_member.go | 4 +- internal/query/iam_member_test.go | 16 +- internal/query/idp.go | 14 +- internal/query/idp_login_policy_link.go | 2 +- internal/query/idp_template.go | 74 +- internal/query/idp_template_test.go | 38 +- internal/query/idp_test.go | 12 +- internal/query/idp_user_link.go | 2 +- internal/query/instance.go | 9 +- internal/query/instance_domain.go | 2 +- internal/query/key.go | 4 +- internal/query/lockout_policy.go | 6 +- internal/query/login_policy.go | 22 +- internal/query/login_policy_test.go | 24 +- internal/query/member.go | 23 +- internal/query/milestone.go | 2 +- internal/query/notification_policy.go | 5 +- internal/query/org.go | 8 +- internal/query/org_domain.go | 2 +- internal/query/org_member.go | 4 +- internal/query/org_member_test.go | 16 +- internal/query/org_metadata.go | 11 +- internal/query/password_age_policy.go | 9 +- internal/query/password_complexity_policy.go | 9 +- internal/query/privacy_policy.go | 9 +- internal/query/project.go | 8 +- internal/query/project_grant.go | 10 +- internal/query/project_grant_member.go | 4 +- internal/query/project_grant_member_test.go | 16 +- internal/query/project_grant_test.go | 40 +- internal/query/project_member.go | 4 +- internal/query/project_member_test.go | 16 +- internal/query/project_role.go | 10 +- internal/query/projection/action.go | 75 +- internal/query/projection/action_test.go | 129 +- internal/query/projection/app.go | 247 +- internal/query/projection/app_test.go | 297 +- internal/query/projection/auth_request.go | 68 +- .../query/projection/auth_request_test.go | 17 +- internal/query/projection/authn_key.go | 83 +- internal/query/projection/authn_key_test.go | 275 +- internal/query/projection/config.go | 8 +- internal/query/projection/custom_text.go | 65 +- internal/query/projection/custom_text_test.go | 131 +- .../query/projection/debug_notification.go | 55 +- .../debug_notification_provider_test.go | 115 +- internal/query/projection/device_auth.go | 68 +- internal/query/projection/domain_policy.go | 67 +- .../query/projection/domain_policy_test.go | 115 +- internal/query/projection/event_test.go | 52 +- .../query/projection/eventstore_mock_test.go | 44 + internal/query/projection/executer_test.go | 4 + internal/query/projection/flow.go | 65 +- internal/query/projection/flow_test.go | 67 +- internal/query/projection/idp.go | 161 +- .../query/projection/idp_login_policy_link.go | 69 +- .../projection/idp_login_policy_link_test.go | 211 +- internal/query/projection/idp_template.go | 673 +- .../query/projection/idp_template_test.go | 1112 ++- internal/query/projection/idp_test.go | 395 +- internal/query/projection/idp_user_link.go | 73 +- .../query/projection/idp_user_link_test.go | 138 +- internal/query/projection/instance.go | 65 +- internal/query/projection/instance_domain.go | 61 +- .../query/projection/instance_domain_test.go | 51 +- internal/query/projection/instance_member.go | 57 +- .../query/projection/instance_member_test.go | 150 +- internal/query/projection/instance_test.go | 83 +- internal/query/projection/key.go | 145 +- internal/query/projection/key_test.go | 69 +- internal/query/projection/label_policy.go | 131 +- .../query/projection/label_policy_test.go | 499 +- internal/query/projection/lockout_policy.go | 65 +- .../query/projection/lockout_policy_test.go | 115 +- internal/query/projection/login_name.go | 176 +- internal/query/projection/login_name_test.go | 291 +- internal/query/projection/login_policy.go | 111 +- .../query/projection/login_policy_test.go | 264 +- internal/query/projection/mail_template.go | 61 +- .../query/projection/mail_template_test.go | 115 +- internal/query/projection/main_test.go | 36 +- internal/query/projection/member.go | 59 +- .../query/projection/message_text_test.go | 307 +- internal/query/projection/message_texts.go | 81 +- internal/query/projection/milestones.go | 120 +- internal/query/projection/milestones_test.go | 93 +- .../query/projection/notification_policy.go | 61 +- .../projection/notification_policy_test.go | 115 +- internal/query/projection/oidc_settings.go | 55 +- .../query/projection/oidc_settings_test.go | 51 +- internal/query/projection/org.go | 70 +- internal/query/projection/org_domain.go | 71 +- internal/query/projection/org_domain_test.go | 115 +- internal/query/projection/org_member.go | 60 +- internal/query/projection/org_member_test.go | 124 +- internal/query/projection/org_metadata.go | 62 +- .../query/projection/org_metadata_test.go | 83 +- internal/query/projection/org_test.go | 133 +- .../query/projection/password_age_policy.go | 65 +- .../projection/password_age_policy_test.go | 115 +- .../projection/password_complexity_policy.go | 71 +- .../password_complexity_policy_test.go | 115 +- internal/query/projection/privacy_policy.go | 69 +- .../query/projection/privacy_policy_test.go | 115 +- internal/query/projection/project.go | 79 +- internal/query/projection/project_grant.go | 93 +- .../query/projection/project_grant_member.go | 76 +- .../projection/project_grant_member_test.go | 193 +- .../query/projection/project_grant_test.go | 153 +- internal/query/projection/project_member.go | 63 +- .../query/projection/project_member_test.go | 166 +- internal/query/projection/project_role.go | 69 +- .../query/projection/project_role_test.go | 117 +- internal/query/projection/project_test.go | 133 +- internal/query/projection/projection.go | 152 +- internal/query/projection/quota.go | 156 +- internal/query/projection/quota_test.go | 52 +- internal/query/projection/secret_generator.go | 63 +- .../query/projection/secret_generator_test.go | 67 +- internal/query/projection/security_policy.go | 45 +- internal/query/projection/session.go | 99 +- internal/query/projection/session_test.go | 71 +- internal/query/projection/sms.go | 93 +- internal/query/projection/sms_test.go | 115 +- internal/query/projection/smtp.go | 65 +- internal/query/projection/smtp_test.go | 76 +- internal/query/projection/user.go | 329 +- internal/query/projection/user_auth_method.go | 81 +- .../query/projection/user_auth_method_test.go | 187 +- internal/query/projection/user_grant.go | 157 +- internal/query/projection/user_grant_test.go | 342 +- internal/query/projection/user_metadata.go | 66 +- .../query/projection/user_metadata_test.go | 99 +- .../projection/user_personal_access_token.go | 72 +- .../user_personal_access_token_test.go | 85 +- internal/query/projection/user_test.go | 659 +- internal/query/query.go | 6 +- internal/query/search_query.go | 68 +- internal/query/secret_generators.go | 2 +- internal/query/security_policy.go | 2 +- internal/query/session.go | 8 +- internal/query/sms.go | 2 +- internal/query/user.go | 41 +- internal/query/user_auth_method.go | 4 +- internal/query/user_grant.go | 15 +- internal/query/user_grant_test.go | 48 +- internal/query/user_membership.go | 112 +- internal/query/user_membership_test.go | 77 +- internal/query/user_metadata.go | 11 +- internal/query/user_otp.go | 1 + internal/query/user_password.go | 1 + internal/query/user_personal_access_token.go | 12 +- .../query/user_personal_access_token_test.go | 16 +- internal/query/user_test.go | 26 +- internal/query/zitadel_permission.go | 2 +- internal/renderer/renderer.go | 6 +- internal/repository/action/action.go | 48 +- internal/repository/asset/asset.go | 19 +- .../repository/authrequest/auth_request.go | 46 +- internal/repository/deviceauth/constraints.go | 12 +- internal/repository/deviceauth/device_auth.go | 16 +- internal/repository/feature/feature.go | 4 +- internal/repository/flow/flow.go | 27 +- internal/repository/idp/apple.go | 19 +- internal/repository/idp/azuread.go | 19 +- internal/repository/idp/github.go | 35 +- internal/repository/idp/gitlab.go | 35 +- internal/repository/idp/google.go | 19 +- internal/repository/idp/idp.go | 11 +- internal/repository/idp/jwt.go | 19 +- internal/repository/idp/ldap.go | 18 +- internal/repository/idp/oauth.go | 19 +- internal/repository/idp/oidc.go | 27 +- internal/repository/idp/saml.go | 19 +- internal/repository/idpconfig/idp_config.go | 55 +- internal/repository/idpconfig/jwt_config.go | 19 +- internal/repository/idpconfig/oidc_config.go | 22 +- internal/repository/idpintent/intent.go | 50 +- internal/repository/instance/custom_text.go | 7 +- .../instance/debug_notification_file.go | 8 +- .../instance/debug_notification_log.go | 8 +- internal/repository/instance/domain.go | 41 +- .../instance/event_default_language.go | 10 +- .../instance/event_iam_project_set.go | 21 +- internal/repository/instance/event_org_set.go | 13 +- internal/repository/instance/idp.go | 57 +- internal/repository/instance/idp_config.go | 14 +- .../repository/instance/idp_jwt_config.go | 6 +- .../repository/instance/idp_oidc_config.go | 8 +- internal/repository/instance/instance.go | 26 +- internal/repository/instance/member.go | 10 +- internal/repository/instance/oidc_settings.go | 18 +- internal/repository/instance/policy_domain.go | 6 +- internal/repository/instance/policy_label.go | 34 +- internal/repository/instance/policy_login.go | 8 +- .../instance/policy_login_factors.go | 12 +- .../policy_login_identity_provider.go | 10 +- .../instance/policy_mail_template.go | 5 +- .../repository/instance/policy_mail_text.go | 5 +- .../instance/policy_notification.go | 5 +- .../instance/policy_password_age.go | 6 +- .../instance/policy_password_complexity.go | 6 +- .../instance/policy_password_lockout.go | 6 +- .../repository/instance/policy_privacy.go | 5 +- .../repository/instance/policy_security.go | 10 +- .../repository/instance/secret_generator.go | 36 +- internal/repository/instance/sms.go | 50 +- internal/repository/instance/smtp_config.go | 34 +- internal/repository/keypair/certificate.go | 10 +- internal/repository/keypair/key_pair.go | 10 +- internal/repository/member/events.go | 46 +- internal/repository/metadata/metadata.go | 25 +- internal/repository/milestone/events.go | 7 +- .../repository/oidcsession/oidc_session.go | 24 +- internal/repository/org/custom_text.go | 8 +- internal/repository/org/domain.go | 63 +- internal/repository/org/flow.go | 7 +- internal/repository/org/idp.go | 57 +- internal/repository/org/idp_config.go | 14 +- internal/repository/org/idp_jwt_config.go | 6 +- internal/repository/org/idp_oidc_config.go | 8 +- internal/repository/org/member.go | 10 +- internal/repository/org/metadata.go | 7 +- internal/repository/org/org.go | 48 +- internal/repository/org/policy_domain.go | 8 +- internal/repository/org/policy_label.go | 35 +- internal/repository/org/policy_login.go | 10 +- .../repository/org/policy_login_factors.go | 12 +- .../org/policy_login_identity_provider.go | 10 +- .../repository/org/policy_mail_template.go | 8 +- internal/repository/org/policy_mail_text.go | 8 +- .../repository/org/policy_notification.go | 7 +- .../repository/org/policy_password_age.go | 8 +- .../org/policy_password_complexity.go | 8 +- .../repository/org/policy_password_lockout.go | 8 +- internal/repository/org/policy_privacy.go | 7 +- internal/repository/policy/custom_text.go | 27 +- internal/repository/policy/label.go | 97 +- internal/repository/policy/login.go | 24 +- internal/repository/policy/mail_template.go | 25 +- internal/repository/policy/mail_text.go | 34 +- internal/repository/policy/policy_domain.go | 28 +- .../repository/policy/policy_login_factors.go | 35 +- .../policy/policy_login_identity_provider.go | 27 +- .../repository/policy/policy_notification.go | 25 +- .../repository/policy/policy_password_age.go | 25 +- .../policy/policy_password_complexity.go | 28 +- .../policy/policy_password_lockout.go | 28 +- internal/repository/policy/policy_privacy.go | 25 +- internal/repository/project/api_config.go | 45 +- internal/repository/project/application.go | 57 +- internal/repository/project/grant.go | 63 +- internal/repository/project/grant_member.go | 49 +- internal/repository/project/key.go | 23 +- internal/repository/project/member.go | 10 +- internal/repository/project/oidc_config.go | 42 +- internal/repository/project/project.go | 57 +- internal/repository/project/role.go | 39 +- internal/repository/project/saml_config.go | 28 +- internal/repository/quota/events.go | 45 +- internal/repository/session/session.go | 93 +- .../repository/settings/debug_notification.go | 25 +- internal/repository/user/human.go | 58 +- internal/repository/user/human_address.go | 13 +- internal/repository/user/human_avatar.go | 5 +- internal/repository/user/human_email.go | 36 +- .../repository/user/human_external_idp.go | 55 +- internal/repository/user/human_mfa_events.go | 8 +- internal/repository/user/human_mfa_otp.go | 89 +- .../repository/user/human_mfa_passwordless.go | 56 +- internal/repository/user/human_mfa_u2f.go | 15 +- .../repository/user/human_mfa_web_auth_n.go | 59 +- internal/repository/user/human_password.go | 50 +- internal/repository/user/human_phone.go | 42 +- internal/repository/user/human_profile.go | 13 +- .../repository/user/human_refresh_token.go | 29 +- internal/repository/user/machine.go | 23 +- internal/repository/user/machine_key.go | 17 +- internal/repository/user/machine_secret.go | 34 +- internal/repository/user/metadata.go | 7 +- .../repository/user/personal_access_token.go | 18 +- internal/repository/user/user.go | 82 +- internal/repository/usergrant/user_grant.go | 62 +- .../eventsourcing/model/auth_request.go | 7 +- .../repository/eventsourcing/model/otp.go | 13 +- .../eventsourcing/model/password.go | 22 +- .../repository/eventsourcing/model/token.go | 18 +- .../repository/eventsourcing/model/user.go | 7 +- .../eventsourcing/model/user_human.go | 3 +- .../eventsourcing/model/user_machine.go | 5 +- .../eventsourcing/model/web_auth_n.go | 43 +- .../repository/view/model/external_idps.go | 6 +- .../user/repository/view/model/notify_user.go | 47 +- .../repository/view/model/notify_user_test.go | 18 +- .../repository/view/model/refresh_token.go | 90 +- internal/user/repository/view/model/token.go | 106 +- internal/user/repository/view/model/user.go | 195 +- .../repository/view/model/user_membership.go | 21 +- .../repository/view/model/user_session.go | 108 +- .../view/model/user_session_test.go | 34 +- .../user/repository/view/model/user_test.go | 73 +- internal/user/repository/view/query.go | 19 +- internal/user/repository/view/user_view.go | 70 - internal/view/model/failed_event.go | 13 - internal/view/model/general_query.go | 25 - internal/view/model/view.go | 13 - internal/view/repository/failed_events.go | 180 - internal/view/repository/general_query.go | 53 - internal/view/repository/query.go | 2 +- internal/view/repository/requests.go | 48 - internal/view/repository/requests_test.go | 167 - internal/view/repository/sequence.go | 216 - 791 files changed, 30326 insertions(+), 43202 deletions(-) delete mode 100644 cmd/setup/04.go delete mode 100644 cmd/setup/04/cockroach/index.sql delete mode 100644 cmd/setup/04/postgres/index.sql delete mode 100644 cmd/setup/09.go delete mode 100644 cmd/setup/09/cockroach/index.sql delete mode 100644 cmd/setup/09/postgres/index.sql delete mode 100644 cmd/setup/10/10_update.sql create mode 100644 cmd/setup/10/cockroach/10_update.sql create mode 100644 cmd/setup/10/postgres/10_update.sql delete mode 100644 cmd/setup/11.go delete mode 100644 cmd/setup/11/11_add_column.sql delete mode 100644 cmd/setup/11/11_set_column.sql delete mode 100644 cmd/setup/11/11_update_events.sql delete mode 100644 cmd/setup/11/cockroach/create_index.sql delete mode 100644 cmd/setup/11/cockroach/drop_index.sql delete mode 100644 cmd/setup/11/postgres/create_index.sql delete mode 100644 cmd/setup/11/postgres/drop_index.sql create mode 100644 cmd/setup/14.go create mode 100644 cmd/setup/14/cockroach/01_disable_inserts.sql create mode 100644 cmd/setup/14/cockroach/02_create_and_fill_events2.sql create mode 100644 cmd/setup/14/cockroach/03_constraints.sql create mode 100644 cmd/setup/14/cockroach/04_indexes.sql create mode 100644 cmd/setup/14/postgres/01_disable_inserts.sql create mode 100644 cmd/setup/14/postgres/02_create_and_fill_events2.sql create mode 100644 cmd/setup/14/postgres/03_events2_pk.sql create mode 100644 cmd/setup/14/postgres/04_constraints.sql create mode 100644 cmd/setup/14/postgres/05_indexes.sql create mode 100644 cmd/setup/15.go create mode 100644 cmd/setup/15/cockroach/01_new_failed_events.sql create mode 100644 cmd/setup/15/cockroach/02_fe_from_projections.sql create mode 100644 cmd/setup/15/cockroach/03_fe_from_adminapi.sql create mode 100644 cmd/setup/15/cockroach/04_fe_from_auth.sql create mode 100644 cmd/setup/15/cockroach/05_current_states.sql create mode 100644 cmd/setup/15/cockroach/06_cs_from_projections.sql create mode 100644 cmd/setup/15/cockroach/07_cs_from_adminapi.sql create mode 100644 cmd/setup/15/cockroach/08_cs_from_auth.sql create mode 100644 cmd/setup/15/postgres/01_new_failed_events.sql create mode 100644 cmd/setup/15/postgres/02_fe_from_projections.sql create mode 100644 cmd/setup/15/postgres/03_fe_from_adminapi.sql create mode 100644 cmd/setup/15/postgres/04_fe_from_auth.sql create mode 100644 cmd/setup/15/postgres/05_current_states.sql create mode 100644 cmd/setup/15/postgres/06_cs_from_projections.sql create mode 100644 cmd/setup/15/postgres/07_cs_from_adminapi.sql create mode 100644 cmd/setup/15/postgres/08_cs_from_auth.sql delete mode 100644 cmd/setup/eventstore_index.go delete mode 100644 internal/admin/repository/administrator.go delete mode 100644 internal/admin/repository/eventsourcing/eventstore/administrator.go delete mode 100644 internal/admin/repository/eventsourcing/spooler/lock.go delete mode 100644 internal/admin/repository/eventsourcing/spooler/spooler.go delete mode 100644 internal/admin/repository/eventsourcing/view/error_event.go delete mode 100644 internal/admin/repository/eventsourcing/view/sequence.go delete mode 100644 internal/admin/repository/repository.go delete mode 100644 internal/api/authz/retry.go delete mode 100644 internal/api/authz/retry_test.go delete mode 100644 internal/api/grpc/admin/failed_event_converter_test.go delete mode 100644 internal/api/grpc/system/failed_event_converter_test.go delete mode 100644 internal/auth/repository/eventsourcing/spooler/lock.go delete mode 100644 internal/auth/repository/eventsourcing/spooler/spooler.go delete mode 100644 internal/auth/repository/eventsourcing/view/error_event.go delete mode 100644 internal/auth/repository/eventsourcing/view/sequence.go delete mode 100644 internal/authz/repository/eventsourcing/view/error_event.go delete mode 100644 internal/authz/repository/eventsourcing/view/sequence.go delete mode 100644 internal/database/cockroach/config.go create mode 100644 internal/database/dialect/connections.go create mode 100644 internal/database/mock/sql_mock.go delete mode 100644 internal/database/postgres/config.go create mode 100644 internal/eventstore/bench_payload.txt create mode 100644 internal/eventstore/eventstore_bench_test.go create mode 100644 internal/eventstore/eventstore_pusher_test.go create mode 100644 internal/eventstore/eventstore_querier_test.go delete mode 100644 internal/eventstore/handler/crdb/current_sequence.go delete mode 100644 internal/eventstore/handler/crdb/failed_stmt.go delete mode 100644 internal/eventstore/handler/crdb/handler_stmt.go delete mode 100644 internal/eventstore/handler/crdb/handler_stmt_test.go delete mode 100644 internal/eventstore/handler/crdb/init_test.go delete mode 100644 internal/eventstore/handler/crdb/reduce.go delete mode 100644 internal/eventstore/handler/handler.go delete mode 100644 internal/eventstore/handler/handler_projection.go delete mode 100644 internal/eventstore/handler/handler_projection_test.go delete mode 100644 internal/eventstore/handler/reduce.go create mode 100644 internal/eventstore/handler/v2/event.go create mode 100644 internal/eventstore/handler/v2/failed_event.go create mode 100644 internal/eventstore/handler/v2/failed_event_get_count.sql create mode 100644 internal/eventstore/handler/v2/failed_event_set.sql create mode 100644 internal/eventstore/handler/v2/handler.go rename internal/eventstore/handler/{crdb => v2}/init.go (83%) create mode 100644 internal/eventstore/handler/v2/log.go create mode 100644 internal/eventstore/handler/v2/mock_test.go create mode 100644 internal/eventstore/handler/v2/reduce.go create mode 100644 internal/eventstore/handler/v2/state.go create mode 100644 internal/eventstore/handler/v2/state_get.sql create mode 100644 internal/eventstore/handler/v2/state_get_await.sql create mode 100644 internal/eventstore/handler/v2/state_lock.sql create mode 100644 internal/eventstore/handler/v2/state_set.sql create mode 100644 internal/eventstore/handler/v2/state_set_last_run.sql create mode 100644 internal/eventstore/handler/v2/state_test.go rename internal/eventstore/handler/{crdb => v2}/statement.go (52%) rename internal/eventstore/handler/{crdb => v2}/statement_test.go (80%) delete mode 100644 internal/eventstore/repository/repository.go delete mode 100644 internal/eventstore/repository/sql/types.go delete mode 100644 internal/eventstore/repository/unique_constraint.go delete mode 100644 internal/eventstore/repository/version.go delete mode 100644 internal/eventstore/v1/eventstore.go delete mode 100644 internal/eventstore/v1/gen_mock.go delete mode 100644 internal/eventstore/v1/internal/repository/gen_mock.go delete mode 100644 internal/eventstore/v1/internal/repository/mock/repository.go delete mode 100644 internal/eventstore/v1/internal/repository/mock/repository.mock.go delete mode 100644 internal/eventstore/v1/internal/repository/repository.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/config.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/db_mock_test.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/filter.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/filter_test.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/query.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/query_test.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/sql.go delete mode 100644 internal/eventstore/v1/internal/repository/sql/types.go delete mode 100644 internal/eventstore/v1/locker/lock.go delete mode 100644 internal/eventstore/v1/locker/lock_test.go delete mode 100644 internal/eventstore/v1/mock/eventstore.mock.go delete mode 100644 internal/eventstore/v1/models/aggregate.go delete mode 100644 internal/eventstore/v1/models/aggregate_creator.go delete mode 100644 internal/eventstore/v1/models/aggregate_creator_test.go delete mode 100644 internal/eventstore/v1/models/aggregate_test.go delete mode 100644 internal/eventstore/v1/models/field.go delete mode 100644 internal/eventstore/v1/models/filter.go delete mode 100644 internal/eventstore/v1/models/filter_test.go delete mode 100644 internal/eventstore/v1/models/operation.go delete mode 100644 internal/eventstore/v1/models/search_query.go delete mode 100644 internal/eventstore/v1/models/search_query_old.go delete mode 100644 internal/eventstore/v1/models/search_query_old_test.go delete mode 100644 internal/eventstore/v1/models/search_query_test.go delete mode 100644 internal/eventstore/v1/models/version.go delete mode 100644 internal/eventstore/v1/models/version_test.go delete mode 100755 internal/eventstore/v1/query/handler.go delete mode 100644 internal/eventstore/v1/sdk/append_event_error.go delete mode 100644 internal/eventstore/v1/sdk/append_event_error_test.go delete mode 100644 internal/eventstore/v1/sdk/sdk.go delete mode 100644 internal/eventstore/v1/sdk/sdk_test.go delete mode 100644 internal/eventstore/v1/spooler/config.go delete mode 100644 internal/eventstore/v1/spooler/mock.go delete mode 100644 internal/eventstore/v1/spooler/mock/spooler.go delete mode 100644 internal/eventstore/v1/spooler/spooler.go delete mode 100644 internal/eventstore/v1/spooler/spooler_test.go delete mode 100644 internal/eventstore/v1/subscription.go create mode 100644 internal/eventstore/v3/event.go create mode 100644 internal/eventstore/v3/event_test.go create mode 100644 internal/eventstore/v3/eventstore.go create mode 100644 internal/eventstore/v3/mock_test.go create mode 100644 internal/eventstore/v3/push.go create mode 100644 internal/eventstore/v3/push.sql create mode 100644 internal/eventstore/v3/push_test.go create mode 100644 internal/eventstore/v3/sequence.go create mode 100644 internal/eventstore/v3/sequence_test.go create mode 100644 internal/eventstore/v3/sequences_query.sql create mode 100644 internal/eventstore/v3/type.go create mode 100644 internal/eventstore/v3/unique_constraints.go create mode 100644 internal/eventstore/v3/unique_constraints_add.sql create mode 100644 internal/eventstore/v3/unique_constraints_delete.sql rename internal/eventstore/{repository => }/version_test.go (96%) delete mode 100644 internal/query/current_sequence.go create mode 100644 internal/query/current_state.go rename internal/query/{current_sequence_test.go => current_state_test.go} (51%) create mode 100644 internal/query/projection/eventstore_mock_test.go delete mode 100644 internal/view/model/failed_event.go delete mode 100644 internal/view/model/general_query.go delete mode 100644 internal/view/model/view.go delete mode 100644 internal/view/repository/failed_events.go delete mode 100644 internal/view/repository/general_query.go delete mode 100644 internal/view/repository/sequence.go diff --git a/.gitignore b/.gitignore index f93505b4b..b2f4277b2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ profile.cov *.out #Debug -__debug_bin +__debug_bin* debug sandbox.go /cmd/dev/ @@ -48,6 +48,7 @@ cmd/zitadel/zitadel tmp/ console/src/app/proto/generated/ **.pb.go +!pkg/grpc/protoc/v2/options.pb.go **.proto.mock.go **.pb.*.go **.gen.go diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 891828941..7897e1283 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -78,13 +78,17 @@ HTTP1HostHeader: "host" # ZITADEL_HTTP1HOSTHEADER WebAuthNName: ZITADEL # ZITADEL_WEBAUTHN_NAME Database: + # This setting defines the ratio of how many connections defined below + # are used to push events. ZITADEL manages two database connection pools + # one to push events and one for the remaining queries. + EventPushConnRatio: 0.2 # ZITADEL_DATABASE_COCKROACH_EVENTPUSHCONNRATIO # CockroachDB is the default database of ZITADEL cockroach: Host: localhost # ZITADEL_DATABASE_COCKROACH_HOST Port: 26257 # ZITADEL_DATABASE_COCKROACH_PORT Database: zitadel # ZITADEL_DATABASE_COCKROACH_DATABASE - MaxOpenConns: 20 # ZITADEL_DATABASE_COCKROACH_MAXOPENCONNS - MaxIdleConns: 10 # ZITADEL_DATABASE_COCKROACH_MAXIDLECONNS + MaxOpenConns: 40 # ZITADEL_DATABASE_COCKROACH_MAXOPENCONNS + MaxIdleConns: 20 # ZITADEL_DATABASE_COCKROACH_MAXIDLECONNS MaxConnLifetime: 30m # ZITADEL_DATABASE_COCKROACH_MAXCONNLIFETIME MaxConnIdleTime: 5m # ZITADEL_DATABASE_COCKROACH_MAXCONNIDLETIME Options: "" # ZITADEL_DATABASE_COCKROACH_OPTIONS @@ -177,14 +181,16 @@ AssetStorage: # The Projections section defines the behavior for the scheduled and synchronous events projections. Projections: + # The maximum duration a transaction remains open + # before it spots left folding additional events + # and updates the table. + TransactionDuration: 500ms # ZITADEL_PROJECTIONS_TRANSACTIONDURATION # Time interval between scheduled projections RequeueEvery: 60s # ZITADEL_PROJECTIONS_REQUEUEEVERY # Time between retried database statements resulting from projected events RetryFailedAfter: 1s # ZITADEL_PROJECTIONS_RETRYFAILED # Retried execution number of database statements resulting from projected events MaxFailureCount: 5 # ZITADEL_PROJECTIONS_MAXFAILURECOUNT - # Number of concurrent projection routines. Values of 0 and below are overwritten to 1 - ConcurrentInstances: 1 # ZITADEL_PROJECTIONS_CONCURRENTINSTANCES # Limit of returned events per query BulkLimit: 200 # ZITADEL_PROJECTIONS_BULKLIMIT # Only instances are projected, for which at least a projection-relevant event exists within the timeframe @@ -194,11 +200,17 @@ Projections: # In the Customizations section, all settings from above can be overwritten for each specific projection Customizations: Projects: - BulkLimit: 2000 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_PROJECTS_BULKLIMIT + TransactionDuration: 2s # The Notifications projection is used for sending emails and SMS to users Notifications: # As notification projections don't result in database statements, retries don't have an effect - MaxFailureCount: 0 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_MAXFAILURECOUNT + MaxFailureCount: 10 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_MAXFAILURECOUNT + # Sending emails can take longer than 500ms + TransactionDuration: 5s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_TRANSACTIONDURATION + password_complexities: + TransactionDuration: 2s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_PASSWORD_COMPLEXITIES_TRANSACTIONDURATION + lockout_policy: + TransactionDuration: 2s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_LOCKOUT_POLICY_TRANSACTIONDURATION # The NotificationsQuotas projection is used for calling quota webhooks NotificationsQuotas: # In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances. @@ -207,9 +219,13 @@ Projections: # Defaults to 45 days HandleActiveInstances: 1080h # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_HANDLEACTIVEINSTANCES # As quota notification projections don't result in database statements, retries don't have an effect - MaxFailureCount: 0 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_MAXFAILURECOUNT + MaxFailureCount: 10 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_MAXFAILURECOUNT # Quota notifications are not so time critical. Setting RequeueEvery every five minutes doesn't annoy the db too much. RequeueEvery: 300s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_REQUEUEEVERY + # Sending emails can take longer than 500ms + TransactionDuration: 5s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONQUOTAS_TRANSACTIONDURATION + milestones: + BulkLimit: 50 # The Telemetry projection is used for calling telemetry webhooks Telemetry: # In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances. @@ -223,20 +239,34 @@ Projections: RequeueEvery: 3300s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_TELEMETRY_REQUEUEEVERY Auth: + # See Projections.BulkLimit SearchLimit: 1000 # ZITADEL_AUTH_SEARCHLIMIT Spooler: - ConcurrentWorkers: 1 # ZITADEL_AUTH_SPOOLER_CONCURRENTWORKERS - ConcurrentInstances: 1 # ZITADEL_AUTH_SPOOLER_CONCURRENTINSTANCES - BulkLimit: 10000 # ZITADEL_AUTH_SPOOLER_BULKLIMIT - FailureCountUntilSkip: 5 # ZITADEL_AUTH_SPOOLER_FAILURECOUNTUNTILSKIP + # See Projections.TransationDuration + TransactionDuration: 10s #ZITADEL_AUTH_SPOOLER_TRANSACTIONDURATION + # See Projections.BulkLimit + BulkLimit: 100 #ZITADEL_AUTH_SPOOLER_BULKLIMIT + # See Projections.MaxFailureCount + FailureCountUntilSkip: 5 #ZITADEL_AUTH_SPOOLER_FAILURECOUNTUNTILSKIP + # Only instance are projected, for which at least a projection relevant event exists withing the timeframe + # from HandleActiveInstances duration in the past until the projections current time + # Defaults to twice the RequeueEvery duration + HandleActiveInstances: 120s #ZITADEL_AUTH_SPOOLER_HANDLEACTIVEINSTANCES Admin: + # See Projections.BulkLimit SearchLimit: 1000 # ZITADEL_ADMIN_SEARCHLIMIT Spooler: - ConcurrentWorkers: 1 # ZITADEL_ADMIN_SPOOLER_CONCURRENTWORKERS - ConcurrentInstances: 1 # ZITADEL_ADMIN_SPOOLER_CONCURRENTINSTANCES - BulkLimit: 10000 # ZITADEL_ADMIN_SPOOLER_BULKLIMIT - FailureCountUntilSkip: 5 # ZITADEL_ADMIN_SPOOLER_FAILURECOUNTUNTILSKIP + # See Projections.TransationDuration + TransactionDuration: 10s + # See Projections.BulkLimit + BulkLimit: 200 + # See Projections.MaxFailureCount + FailureCountUntilSkip: 5 + # Only instance are projected, for which at least a projection relevant event exists withing the timeframe + # from HandleActiveInstances duration in the past until the projections current time + # Defaults to twice the RequeueEvery duration + HandleActiveInstances: 120s UserAgentCookie: Name: zitadel.useragent # ZITADEL_USERAGENTCOOKIE_NAME @@ -322,10 +352,12 @@ Console: Notification: Repository: Spooler: - ConcurrentWorkers: 1 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_CONCURRENTWORKERS - ConcurrentInstances: 10 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_CONCURRENTINSTANCES - BulkLimit: 10000 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_BULKLIMIT - FailureCountUntilSkip: 5 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_FAILURECOUNTUNTILSKIP + # See Projections.TransactionDuration + TransactionDuration: 10s #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_TRANSACTIONDURATION + # See Projections.BulkLimit + BulkLimit: 200 #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_BULKLIMIT + # See Projections.MaxFailureCount + FailureCountUntilSkip: 5 #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_FAILURECOUNTUNTILSKIP Handlers: EncryptionKeys: @@ -477,8 +509,8 @@ Quotas: MaxBulkSize: 0 # ZITADEL_QUOTAS_EXECUTION_DEBOUNCE_MAXBULKSIZE Eventstore: - PushTimeout: 15s # ZITADEL_EVENTSTORE_PUSHTIMEOUT - AllowOrderByCreationDate: false # ZITADEL_EVENTSTORE_ALLOWORDERBYCREATIONDATE + # Sets the maximum duration of transactions pushing events + PushTimeout: 15s #ZITADEL_EVENTSTORE_PUSHTIMEOUT DefaultInstance: InstanceName: ZITADEL # ZITADEL_DEFAULTINSTANCE_INSTANCENAME diff --git a/cmd/initialise/helper.go b/cmd/initialise/helper.go index 943d16508..6c4abd0eb 100644 --- a/cmd/initialise/helper.go +++ b/cmd/initialise/helper.go @@ -1,13 +1,13 @@ package initialise import ( - "database/sql" "errors" "github.com/jackc/pgconn" + "github.com/zitadel/zitadel/internal/database" ) -func exec(db *sql.DB, stmt string, possibleErrCodes []string, args ...interface{}) error { +func exec(db *database.DB, stmt string, possibleErrCodes []string, args ...interface{}) error { _, err := db.Exec(stmt, args...) pgErr := new(pgconn.PgError) if errors.As(err, &pgErr) { diff --git a/cmd/initialise/init.go b/cmd/initialise/init.go index 78baf6c20..69aadd87b 100644 --- a/cmd/initialise/init.go +++ b/cmd/initialise/init.go @@ -1,7 +1,6 @@ package initialise import ( - "database/sql" "embed" "github.com/spf13/cobra" @@ -68,7 +67,7 @@ func InitAll(config *Config) { logging.OnError(err).Fatal("unable to initialize ZITADEL") } -func initialise(config database.Config, steps ...func(*sql.DB) error) error { +func initialise(config database.Config, steps ...func(*database.DB) error) error { logging.Info("initialization started") err := ReadStmts(config.Type()) @@ -76,16 +75,16 @@ func initialise(config database.Config, steps ...func(*sql.DB) error) error { return err } - db, err := database.Connect(config, true) + db, err := database.Connect(config, true, false) if err != nil { return err } defer db.Close() - return Init(db.DB, steps...) + return Init(db, steps...) } -func Init(db *sql.DB, steps ...func(*sql.DB) error) error { +func Init(db *database.DB, steps ...func(*database.DB) error) error { for _, step := range steps { if err := step(db); err != nil { return err diff --git a/cmd/initialise/init_test.go b/cmd/initialise/init_test.go index 4a1141345..5e35c2a38 100644 --- a/cmd/initialise/init_test.go +++ b/cmd/initialise/init_test.go @@ -1,17 +1,17 @@ package initialise import ( - "database/sql" "database/sql/driver" "regexp" "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/zitadel/zitadel/internal/database" ) type db struct { mock sqlmock.Sqlmock - db *sql.DB + db *database.DB } func prepareDB(t *testing.T, expectations ...expectation) db { @@ -25,7 +25,7 @@ func prepareDB(t *testing.T, expectations ...expectation) db { } return db{ mock: mock, - db: client, + db: &database.DB{DB: client}, } } @@ -42,6 +42,20 @@ func expectExec(stmt string, err error, args ...driver.Value) expectation { } } +func expectQuery(stmt string, err error, columns []string, rows [][]driver.Value, args ...driver.Value) expectation { + return func(m sqlmock.Sqlmock) { + res := sqlmock.NewRows(columns) + for _, row := range rows { + res.AddRow(row...) + } + query := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...).WillReturnRows(res) + if err != nil { + query.WillReturnError(err) + return + } + } +} + func expectBegin(err error) expectation { return func(m sqlmock.Sqlmock) { query := m.ExpectBegin() diff --git a/cmd/initialise/sql/cockroach/03_grant_user.sql b/cmd/initialise/sql/cockroach/03_grant_user.sql index e21832393..161b2f5ba 100644 --- a/cmd/initialise/sql/cockroach/03_grant_user.sql +++ b/cmd/initialise/sql/cockroach/03_grant_user.sql @@ -1,3 +1,4 @@ -- replace the first %[1]s with the database -- replace the second \%[2]s with the user -GRANT ALL ON DATABASE %[1]s TO %[2]s \ No newline at end of file +GRANT ALL ON DATABASE %[1]s TO %[2]s; +GRANT SYSTEM VIEWACTIVITY TO %[2]s; \ No newline at end of file diff --git a/cmd/initialise/sql/cockroach/08_events_table.sql b/cmd/initialise/sql/cockroach/08_events_table.sql index 78c1fba58..00eba689f 100644 --- a/cmd/initialise/sql/cockroach/08_events_table.sql +++ b/cmd/initialise/sql/cockroach/08_events_table.sql @@ -1,27 +1,21 @@ -SET experimental_enable_hash_sharded_indexes = on; +CREATE TABLE IF NOT EXISTS eventstore.events2 ( + instance_id TEXT NOT NULL + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + + , event_type TEXT NOT NULL + , "sequence" BIGINT NOT NULL + , revision SMALLINT NOT NULL + , created_at TIMESTAMPTZ NOT NULL + , payload JSONB + , creator TEXT NOT NULL + , "owner" TEXT NOT NULL + + , "position" DECIMAL NOT NULL + , in_tx_order INTEGER NOT NULL -CREATE TABLE IF NOT EXISTS eventstore.events ( - id UUID DEFAULT gen_random_uuid() - , event_type TEXT NOT NULL - , aggregate_type TEXT NOT NULL - , aggregate_id TEXT NOT NULL - , aggregate_version TEXT NOT NULL - , event_sequence BIGINT NOT NULL - , previous_aggregate_sequence BIGINT - , previous_aggregate_type_sequence INT8 - , creation_date TIMESTAMPTZ NOT NULL DEFAULT now() - , event_data JSONB - , editor_user TEXT NOT NULL - , editor_service TEXT NOT NULL - , resource_owner TEXT NOT NULL - , instance_id TEXT NOT NULL - - , PRIMARY KEY (event_sequence DESC, instance_id) USING HASH WITH BUCKET_COUNT = 10 - , INDEX agg_type_agg_id (aggregate_type, aggregate_id, instance_id) - , INDEX agg_type (aggregate_type, instance_id) - , INDEX agg_type_seq (aggregate_type, event_sequence DESC, instance_id) - STORING (id, event_type, aggregate_id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, resource_owner, previous_aggregate_type_sequence) - , INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC, instance_id) - , CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC, instance_id) - , CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id) -); \ No newline at end of file + , PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence") + , INDEX es_active_instances (created_at DESC) STORING ("position") + , INDEX es_wm (aggregate_id, instance_id, aggregate_type, event_type) + , INDEX es_projection (instance_id, aggregate_type, event_type, "position" DESC) +); diff --git a/cmd/initialise/sql/postgres/08_events_table.sql b/cmd/initialise/sql/postgres/08_events_table.sql index 61474e077..d978df5d9 100644 --- a/cmd/initialise/sql/postgres/08_events_table.sql +++ b/cmd/initialise/sql/postgres/08_events_table.sql @@ -1,25 +1,22 @@ -CREATE TABLE IF NOT EXISTS eventstore.events ( - id UUID DEFAULT gen_random_uuid() - , event_type TEXT NOT NULL - , aggregate_type TEXT NOT NULL - , aggregate_id TEXT NOT NULL - , aggregate_version TEXT NOT NULL - , event_sequence BIGINT NOT NULL - , previous_aggregate_sequence BIGINT - , previous_aggregate_type_sequence INT8 - , creation_date TIMESTAMPTZ NOT NULL DEFAULT now() - , event_data JSONB - , editor_user TEXT NOT NULL - , editor_service TEXT NOT NULL - , resource_owner TEXT NOT NULL - , instance_id TEXT NOT NULL +CREATE TABLE IF NOT EXISTS eventstore.events2 ( + instance_id TEXT NOT NULL + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + + , event_type TEXT NOT NULL + , "sequence" BIGINT NOT NULL + , revision SMALLINT NOT NULL + , created_at TIMESTAMPTZ NOT NULL + , payload JSONB + , creator TEXT NOT NULL + , "owner" TEXT NOT NULL + + , "position" DECIMAL NOT NULL + , in_tx_order INTEGER NOT NULL - , PRIMARY KEY (event_sequence, instance_id) - , CONSTRAINT previous_sequence_unique UNIQUE(previous_aggregate_sequence, instance_id) - , CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id) + , PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence") ); -CREATE INDEX IF NOT EXISTS agg_type_agg_id ON eventstore.events (aggregate_type, aggregate_id, instance_id); -CREATE INDEX IF NOT EXISTS agg_type ON eventstore.events (aggregate_type, instance_id); -CREATE INDEX IF NOT EXISTS agg_type_seq ON eventstore.events (aggregate_type, event_sequence DESC, instance_id); -CREATE INDEX IF NOT EXISTS max_sequence ON eventstore.events (aggregate_type, aggregate_id, event_sequence DESC, instance_id); +CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id); +CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type); +CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position"); \ No newline at end of file diff --git a/cmd/initialise/verify_database.go b/cmd/initialise/verify_database.go index 5bdb8a142..089d34582 100644 --- a/cmd/initialise/verify_database.go +++ b/cmd/initialise/verify_database.go @@ -1,13 +1,14 @@ package initialise import ( - "database/sql" _ "embed" "fmt" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" ) func newDatabase() *cobra.Command { @@ -33,8 +34,8 @@ The user provided by flags needs priviledge to } } -func VerifyDatabase(databaseName string) func(*sql.DB) error { - return func(db *sql.DB) error { +func VerifyDatabase(databaseName string) func(*database.DB) error { + return func(db *database.DB) error { logging.WithFields("database", databaseName).Info("verify database") return exec(db, fmt.Sprintf(string(databaseStmt), databaseName), []string{dbAlreadyExistsCode}) diff --git a/cmd/initialise/verify_grant.go b/cmd/initialise/verify_grant.go index 5b89e9b72..b7acc15d9 100644 --- a/cmd/initialise/verify_grant.go +++ b/cmd/initialise/verify_grant.go @@ -1,13 +1,14 @@ package initialise import ( - "database/sql" _ "embed" "fmt" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" ) func newGrant() *cobra.Command { @@ -28,8 +29,8 @@ Prereqesits: } } -func VerifyGrant(databaseName, username string) func(*sql.DB) error { - return func(db *sql.DB) error { +func VerifyGrant(databaseName, username string) func(*database.DB) error { + return func(db *database.DB) error { logging.WithFields("user", username, "database", databaseName).Info("verify grant") return exec(db, fmt.Sprintf(grantStmt, databaseName, username), nil) diff --git a/cmd/initialise/verify_user.go b/cmd/initialise/verify_user.go index 702b51f12..c2be12b7e 100644 --- a/cmd/initialise/verify_user.go +++ b/cmd/initialise/verify_user.go @@ -1,13 +1,14 @@ package initialise import ( - "database/sql" _ "embed" "fmt" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" ) func newUser() *cobra.Command { @@ -33,8 +34,8 @@ The user provided by flags needs priviledge to } } -func VerifyUser(username, password string) func(*sql.DB) error { - return func(db *sql.DB) error { +func VerifyUser(username, password string) func(*database.DB) error { + return func(db *database.DB) error { logging.WithFields("username", username).Info("verify user") if password != "" { diff --git a/cmd/initialise/verify_zitadel.go b/cmd/initialise/verify_zitadel.go index 74fe2a246..1128227ce 100644 --- a/cmd/initialise/verify_zitadel.go +++ b/cmd/initialise/verify_zitadel.go @@ -1,14 +1,13 @@ package initialise import ( - "database/sql" _ "embed" "fmt" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/database" ) @@ -29,58 +28,66 @@ Prereqesits: } } -func VerifyZitadel(db *sql.DB, config database.Config) error { +func VerifyZitadel(db *database.DB, config database.Config) error { err := ReadStmts(config.Type()) if err != nil { return err } + logging.WithFields().Info("verify system") if err := exec(db, fmt.Sprintf(createSystemStmt, config.Username()), nil); err != nil { return err } + logging.WithFields().Info("verify encryption keys") if err := createEncryptionKeys(db); err != nil { return err } + logging.WithFields().Info("verify projections") if err := exec(db, fmt.Sprintf(createProjectionsStmt, config.Username()), nil); err != nil { return err } + logging.WithFields().Info("verify eventstore") if err := exec(db, fmt.Sprintf(createEventstoreStmt, config.Username()), nil); err != nil { return err } + logging.WithFields().Info("verify events tables") if err := createEvents(db); err != nil { return err } + logging.WithFields().Info("verify system sequence") if err := exec(db, createSystemSequenceStmt, nil); err != nil { return err } + logging.WithFields().Info("verify unique constraints") if err := exec(db, createUniqueConstraints, nil); err != nil { return err } + return nil } func verifyZitadel(config database.Config) error { logging.WithFields("database", config.DatabaseName()).Info("verify zitadel") - db, err := database.Connect(config, false) + db, err := database.Connect(config, false, false) if err != nil { return err } - if err := VerifyZitadel(db.DB, config); err != nil { + if err := VerifyZitadel(db, config); err != nil { return err } return db.Close() } -func createEncryptionKeys(db *sql.DB) error { +func createEncryptionKeys(db *database.DB) error { tx, err := db.Begin() if err != nil { return err @@ -93,16 +100,29 @@ func createEncryptionKeys(db *sql.DB) error { return tx.Commit() } -func createEvents(db *sql.DB) error { +func createEvents(db *database.DB) (err error) { tx, err := db.Begin() if err != nil { return err } + defer func() { + if err != nil { + rollbackErr := tx.Rollback() + logging.OnError(rollbackErr).Debug("rollback failed") + return + } + err = tx.Commit() + }() - if _, err = tx.Exec(createEventsStmt); err != nil { - tx.Rollback() + // if events already exists events2 is created during a setup job + var count int + row := tx.QueryRow("SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'") + if err = row.Scan(&count); err != nil { return err } - - return tx.Commit() + if row.Err() != nil || count >= 1 { + return row.Err() + } + _, err = tx.Exec(createEventsStmt) + return err } diff --git a/cmd/initialise/verify_zitadel_test.go b/cmd/initialise/verify_zitadel_test.go index d4dd552d3..4631396e2 100644 --- a/cmd/initialise/verify_zitadel_test.go +++ b/cmd/initialise/verify_zitadel_test.go @@ -2,6 +2,7 @@ package initialise import ( "database/sql" + "database/sql/driver" "errors" "testing" ) @@ -30,11 +31,53 @@ func Test_verifyEvents(t *testing.T) { }, targetErr: sql.ErrConnDone, }, + { + name: "events already exists", + args: args{ + db: prepareDB(t, + expectBegin(nil), + expectQuery( + "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'", + nil, + []string{"count"}, + [][]driver.Value{ + {1}, + }, + ), + expectCommit(nil), + ), + }, + }, + { + name: "events and events2 already exists", + args: args{ + db: prepareDB(t, + expectBegin(nil), + expectQuery( + "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'", + nil, + []string{"count"}, + [][]driver.Value{ + {2}, + }, + ), + expectCommit(nil), + ), + }, + }, { name: "create table fails", args: args{ db: prepareDB(t, expectBegin(nil), + expectQuery( + "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'", + nil, + []string{"count"}, + [][]driver.Value{ + {0}, + }, + ), expectExec(createEventsStmt, sql.ErrNoRows), expectRollback(nil), ), @@ -46,6 +89,14 @@ func Test_verifyEvents(t *testing.T) { args: args{ db: prepareDB(t, expectBegin(nil), + expectQuery( + "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'", + nil, + []string{"count"}, + [][]driver.Value{ + {0}, + }, + ), expectExec(createEventsStmt, nil), expectCommit(nil), ), diff --git a/cmd/key/key.go b/cmd/key/key.go index 02fa272a8..5abd3a47c 100644 --- a/cmd/key/key.go +++ b/cmd/key/key.go @@ -124,7 +124,7 @@ func openFile(fileName string) (io.Reader, error) { } func keyStorage(config database.Config, masterKey string) (crypto.KeyStorage, error) { - db, err := database.Connect(config, false) + db, err := database.Connect(config, false, false) if err != nil { return nil, err } diff --git a/cmd/setup/01_sql/adminapi.sql b/cmd/setup/01_sql/adminapi.sql index 1ef135e74..cb0db84af 100644 --- a/cmd/setup/01_sql/adminapi.sql +++ b/cmd/setup/01_sql/adminapi.sql @@ -12,7 +12,7 @@ CREATE TABLE adminapi.locks ( CREATE TABLE adminapi.current_sequences ( view_name TEXT, current_sequence BIGINT, - event_timestamp TIMESTAMPTZ, + event_date TIMESTAMPTZ, last_successful_spooler_run TIMESTAMPTZ, instance_id TEXT NOT NULL, diff --git a/cmd/setup/01_sql/auth.sql b/cmd/setup/01_sql/auth.sql index 0a2bba226..93a06c599 100644 --- a/cmd/setup/01_sql/auth.sql +++ b/cmd/setup/01_sql/auth.sql @@ -12,7 +12,7 @@ CREATE TABLE auth.locks ( CREATE TABLE auth.current_sequences ( view_name TEXT, current_sequence BIGINT, - event_timestamp TIMESTAMPTZ, + event_date TIMESTAMPTZ, last_successful_spooler_run TIMESTAMPTZ, instance_id TEXT NOT NULL, diff --git a/cmd/setup/04.go b/cmd/setup/04.go deleted file mode 100644 index e9af05ee4..000000000 --- a/cmd/setup/04.go +++ /dev/null @@ -1,23 +0,0 @@ -package setup - -import ( - "embed" - - "github.com/zitadel/zitadel/internal/database" -) - -var ( - //go:embed 04/cockroach/index.sql - //go:embed 04/postgres/index.sql - stmts04 embed.FS -) - -func New04(db *database.DB) *EventstoreIndexesNew { - return &EventstoreIndexesNew{ - dbClient: db, - name: "04_eventstore_indexes", - step: "04", - fileName: "index.sql", - stmts: stmts04, - } -} diff --git a/cmd/setup/04/cockroach/index.sql b/cmd/setup/04/cockroach/index.sql deleted file mode 100644 index f68ed948a..000000000 --- a/cmd/setup/04/cockroach/index.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE INDEX IF NOT EXISTS write_model ON eventstore.events (instance_id, aggregate_type, aggregate_id, event_type, resource_owner) - STORING (id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, previous_aggregate_type_sequence); - -CREATE INDEX IF NOT EXISTS active_instances ON eventstore.events (creation_date desc, instance_id) USING HASH; diff --git a/cmd/setup/04/postgres/index.sql b/cmd/setup/04/postgres/index.sql deleted file mode 100644 index d4c933ece..000000000 --- a/cmd/setup/04/postgres/index.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE INDEX IF NOT EXISTS write_model ON eventstore.events (instance_id, aggregate_type, aggregate_id, event_type, resource_owner); - -CREATE INDEX IF NOT EXISTS active_instances ON eventstore.events (creation_date, instance_id); diff --git a/cmd/setup/09.go b/cmd/setup/09.go deleted file mode 100644 index 1d12a86a7..000000000 --- a/cmd/setup/09.go +++ /dev/null @@ -1,23 +0,0 @@ -package setup - -import ( - "embed" - - "github.com/zitadel/zitadel/internal/database" -) - -var ( - //go:embed 09/cockroach/index.sql - //go:embed 09/postgres/index.sql - stmts09 embed.FS -) - -func New09(db *database.DB) *EventstoreIndexesNew { - return &EventstoreIndexesNew{ - dbClient: db, - name: "09_optimise_indexes", - step: "09", - fileName: "index.sql", - stmts: stmts09, - } -} diff --git a/cmd/setup/09/cockroach/index.sql b/cmd/setup/09/cockroach/index.sql deleted file mode 100644 index 1cc161ed1..000000000 --- a/cmd/setup/09/cockroach/index.sql +++ /dev/null @@ -1,51 +0,0 @@ --- replace agg_type_agg_id -BEGIN; -DROP INDEX IF EXISTS eventstore.events@agg_type_agg_id; -COMMIT; - -BEGIN; -CREATE INDEX agg_type_agg_id ON eventstore.events ( - instance_id - , aggregate_type - , aggregate_id -) STORING ( - event_type - , aggregate_version - , previous_aggregate_sequence - , previous_aggregate_type_sequence - , creation_date - , event_data - , editor_user - , editor_service - , resource_owner -); -COMMIT; - --- replace agg_type -BEGIN; -DROP INDEX IF EXISTS eventstore.events@agg_type; -COMMIT; - -BEGIN; -CREATE INDEX agg_type ON eventstore.events ( - instance_id - , aggregate_type - , event_sequence -) STORING ( - event_type - , aggregate_id - , aggregate_version - , previous_aggregate_sequence - , previous_aggregate_type_sequence - , creation_date - , event_data - , editor_user - , editor_service - , resource_owner -); -COMMIT; - --- drop unused index -BEGIN; -DROP INDEX IF EXISTS eventstore.events@agg_type_seq; -COMMIT; diff --git a/cmd/setup/09/postgres/index.sql b/cmd/setup/09/postgres/index.sql deleted file mode 100644 index d5207c15d..000000000 --- a/cmd/setup/09/postgres/index.sql +++ /dev/null @@ -1,30 +0,0 @@ --- replace agg_type_agg_id -BEGIN; -DROP INDEX IF EXISTS eventstore.agg_type_agg_id; -COMMIT; - -BEGIN; -CREATE INDEX agg_type_agg_id ON eventstore.events ( - instance_id - , aggregate_type - , aggregate_id -); -COMMIT; - --- replace agg_type -BEGIN; -DROP INDEX IF EXISTS eventstore.agg_type; -COMMIT; - -BEGIN; -CREATE INDEX agg_type ON eventstore.events ( - instance_id - , aggregate_type - , event_sequence -); -COMMIT; - --- drop unused index -BEGIN; -DROP INDEX IF EXISTS eventstore.agg_type_seq; -COMMIT; diff --git a/cmd/setup/10.go b/cmd/setup/10.go index 16d14d3d0..9f3d89cbd 100644 --- a/cmd/setup/10.go +++ b/cmd/setup/10.go @@ -3,7 +3,7 @@ package setup import ( "context" "database/sql" - _ "embed" + "embed" "time" "github.com/cockroachdb/cockroach-go/v2/crdb" @@ -17,8 +17,9 @@ var ( correctCreationDate10CreateTable string //go:embed 10/10_fill_table.sql correctCreationDate10FillTable string - //go:embed 10/10_update.sql - correctCreationDate10Update string + //go:embed 10/cockroach/10_update.sql + //go:embed 10/postgres/10_update.sql + correctCreationDate10Update embed.FS //go:embed 10/10_count_wrong_events.sql correctCreationDate10CountWrongEvents string //go:embed 10/10_empty_table.sql @@ -34,7 +35,8 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { ctx, cancel := context.WithTimeout(ctx, mig.FailAfter) defer cancel() - for { + for i := 0; ; i++ { + logging.WithFields("mig", mig.String(), "iteration", i).Debug("start iteration") var affected int64 err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { if mig.dbClient.Type() == "cockroach" { @@ -46,6 +48,7 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { if err != nil { return err } + logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table created") _, err = tx.ExecContext(ctx, correctCreationDate10Truncate) if err != nil { @@ -55,19 +58,25 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { if err != nil { return err } + logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table filled") res := tx.QueryRowContext(ctx, correctCreationDate10CountWrongEvents) if err := res.Scan(&affected); err != nil || affected == 0 { return err } - _, err = tx.ExecContext(ctx, correctCreationDate10Update) + updateStmt, err := readStmt(correctCreationDate10Update, "10", mig.dbClient.Type(), "10_update.sql") if err != nil { return err } - logging.WithFields("count", affected).Info("creation dates changed") + _, err = tx.ExecContext(ctx, updateStmt) + if err != nil { + return err + } + logging.WithFields("mig", mig.String(), "iteration", i, "count", affected).Debug("creation dates updated") return nil }) + logging.WithFields("mig", mig.String(), "iteration", i).Debug("end iteration") if affected == 0 || err != nil { return err } diff --git a/cmd/setup/10/10_create_temp_table.sql b/cmd/setup/10/10_create_temp_table.sql index fb823b2ec..702cd072a 100644 --- a/cmd/setup/10/10_create_temp_table.sql +++ b/cmd/setup/10/10_create_temp_table.sql @@ -1,4 +1,4 @@ -CREATE temporary TABLE IF NOT EXISTS wrong_events ( +CREATE TEMPORARY TABLE IF NOT EXISTS wrong_events ( instance_id TEXT , event_sequence BIGINT , current_cd TIMESTAMPTZ diff --git a/cmd/setup/10/10_fill_table.sql b/cmd/setup/10/10_fill_table.sql index b2ac9e307..2db4c8589 100644 --- a/cmd/setup/10/10_fill_table.sql +++ b/cmd/setup/10/10_fill_table.sql @@ -10,6 +10,8 @@ INSERT INTO wrong_events ( ) AS next_cd FROM eventstore.events + WHERE + "position" IS NULL ) sub WHERE current_cd < next_cd ORDER BY diff --git a/cmd/setup/10/10_update.sql b/cmd/setup/10/10_update.sql deleted file mode 100644 index 5254ba8dd..000000000 --- a/cmd/setup/10/10_update.sql +++ /dev/null @@ -1 +0,0 @@ -UPDATE eventstore.events e SET creation_date = we.next_cd FROM wrong_events we WHERE e.event_sequence = we.event_sequence and e.instance_id = we.instance_id; diff --git a/cmd/setup/10/cockroach/10_update.sql b/cmd/setup/10/cockroach/10_update.sql new file mode 100644 index 000000000..9e7d7f993 --- /dev/null +++ b/cmd/setup/10/cockroach/10_update.sql @@ -0,0 +1 @@ +UPDATE eventstore.events e SET (creation_date, "position") = (we.next_cd, we.next_cd::DECIMAL) FROM wrong_events we WHERE e.event_sequence = we.event_sequence AND e.instance_id = we.instance_id; diff --git a/cmd/setup/10/postgres/10_update.sql b/cmd/setup/10/postgres/10_update.sql new file mode 100644 index 000000000..bc61b692c --- /dev/null +++ b/cmd/setup/10/postgres/10_update.sql @@ -0,0 +1,10 @@ +UPDATE + eventstore.events e +SET + creation_date = we.next_cd + , "position" = (EXTRACT(EPOCH FROM we.next_cd)) +FROM + wrong_events we +WHERE + e.event_sequence = we.event_sequence + AND e.instance_id = we.instance_id; diff --git a/cmd/setup/11.go b/cmd/setup/11.go deleted file mode 100644 index 917f479e0..000000000 --- a/cmd/setup/11.go +++ /dev/null @@ -1,92 +0,0 @@ -package setup - -import ( - "context" - "database/sql" - "embed" - - "github.com/cockroachdb/cockroach-go/v2/crdb" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/database" -) - -var ( - //go:embed 11/11_add_column.sql - addEventCreatedAt string - //go:embed 11/11_update_events.sql - setCreatedAt string - //go:embed 11/11_set_column.sql - setCreatedAtDetails string - //go:embed 11/postgres/create_index.sql - //go:embed 11/cockroach/create_index.sql - createdAtIndexCreateStmt embed.FS - //go:embed 11/postgres/drop_index.sql - //go:embed 11/cockroach/drop_index.sql - createdAtIndexDropStmt embed.FS -) - -type AddEventCreatedAt struct { - BulkAmount int - step10 *CorrectCreationDate - dbClient *database.DB -} - -func (mig *AddEventCreatedAt) Execute(ctx context.Context) error { - // execute step 10 again because events created after the first execution of step 10 - // could still have the wrong ordering of sequences and creation date - if err := mig.step10.Execute(ctx); err != nil { - return err - } - _, err := mig.dbClient.ExecContext(ctx, addEventCreatedAt) - if err != nil { - return err - } - - createIndex, err := readStmt(createdAtIndexCreateStmt, "11", mig.dbClient.Type(), "create_index.sql") - if err != nil { - return err - } - _, err = mig.dbClient.ExecContext(ctx, createIndex) - if err != nil { - return err - } - - for i := 0; ; i++ { - var affected int64 - err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error { - res, err := tx.Exec(setCreatedAt, mig.BulkAmount) - if err != nil { - return err - } - - affected, _ = res.RowsAffected() - return nil - }) - if err != nil { - return err - } - logging.WithFields("step", "11", "iteration", i, "affected", affected).Info("set created_at iteration done") - if affected < int64(mig.BulkAmount) { - break - } - } - - logging.Info("set details") - _, err = mig.dbClient.ExecContext(ctx, setCreatedAtDetails) - if err != nil { - return err - } - - dropIndex, err := readStmt(createdAtIndexDropStmt, "11", mig.dbClient.Type(), "drop_index.sql") - if err != nil { - return err - } - _, err = mig.dbClient.ExecContext(ctx, dropIndex) - - return err -} - -func (mig *AddEventCreatedAt) String() string { - return "11_event_created_at" -} diff --git a/cmd/setup/11/11_add_column.sql b/cmd/setup/11/11_add_column.sql deleted file mode 100644 index 0ba49b3d1..000000000 --- a/cmd/setup/11/11_add_column.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; --- create table with empty created_at -ALTER TABLE eventstore.events ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NULL; --- set column rules -ALTER TABLE eventstore.events ALTER COLUMN created_at SET DEFAULT clock_timestamp(); -COMMIT; \ No newline at end of file diff --git a/cmd/setup/11/11_set_column.sql b/cmd/setup/11/11_set_column.sql deleted file mode 100644 index 108bd182b..000000000 --- a/cmd/setup/11/11_set_column.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -ALTER TABLE eventstore.events ALTER COLUMN created_at SET NOT NULL; -COMMIT; \ No newline at end of file diff --git a/cmd/setup/11/11_update_events.sql b/cmd/setup/11/11_update_events.sql deleted file mode 100644 index 15a3df8a0..000000000 --- a/cmd/setup/11/11_update_events.sql +++ /dev/null @@ -1,21 +0,0 @@ -UPDATE eventstore.events SET - created_at = creation_date -FROM ( - SELECT - e.event_sequence as seq - , e.instance_id as i_id - , e.creation_date as cd - FROM - eventstore.events e - WHERE - created_at IS NULL - ORDER BY - event_sequence ASC - , instance_id - LIMIT $1 -) AS e -WHERE - e.seq = eventstore.events.event_sequence - AND e.i_id = eventstore.events.instance_id - AND e.cd = eventstore.events.creation_date -; \ No newline at end of file diff --git a/cmd/setup/11/cockroach/create_index.sql b/cmd/setup/11/cockroach/create_index.sql deleted file mode 100644 index 6fdd768a3..000000000 --- a/cmd/setup/11/cockroach/create_index.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events ( - event_sequence DESC - , instance_id -) STORING ( - id - , creation_date - , created_at -) WHERE created_at IS NULL; \ No newline at end of file diff --git a/cmd/setup/11/cockroach/drop_index.sql b/cmd/setup/11/cockroach/drop_index.sql deleted file mode 100644 index da91018d7..000000000 --- a/cmd/setup/11/cockroach/drop_index.sql +++ /dev/null @@ -1 +0,0 @@ -DROP INDEX IF EXISTS eventstore.events@ca_fill_idx; \ No newline at end of file diff --git a/cmd/setup/11/postgres/create_index.sql b/cmd/setup/11/postgres/create_index.sql deleted file mode 100644 index a6fb37c99..000000000 --- a/cmd/setup/11/postgres/create_index.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events ( - event_sequence DESC - , instance_id -) WHERE created_at IS NULL; \ No newline at end of file diff --git a/cmd/setup/11/postgres/drop_index.sql b/cmd/setup/11/postgres/drop_index.sql deleted file mode 100644 index acfe2f49b..000000000 --- a/cmd/setup/11/postgres/drop_index.sql +++ /dev/null @@ -1 +0,0 @@ -DROP INDEX IF EXISTS eventstore.ca_fill_idx; \ No newline at end of file diff --git a/cmd/setup/14.go b/cmd/setup/14.go new file mode 100644 index 000000000..2647d83ee --- /dev/null +++ b/cmd/setup/14.go @@ -0,0 +1,72 @@ +package setup + +import ( + "context" + "database/sql" + "embed" + "errors" + "strings" + + "github.com/jackc/pgconn" + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" +) + +var ( + //go:embed 14/cockroach/*.sql + //go:embed 14/postgres/*.sql + newEventsTable embed.FS +) + +type NewEventsTable struct { + dbClient *database.DB +} + +func (mig *NewEventsTable) Execute(ctx context.Context) error { + migrations, err := newEventsTable.ReadDir("14/" + mig.dbClient.Type()) + if err != nil { + return err + } + // if events already exists events2 is created during a setup job + var count int + err = mig.dbClient.QueryRow( + func(row *sql.Row) error { + if err = row.Scan(&count); err != nil { + return err + } + return row.Err() + }, + "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events2'", + ) + if err != nil || count == 1 { + return err + } + for _, migration := range migrations { + stmt, err := readStmt(newEventsTable, "14", mig.dbClient.Type(), migration.Name()) + if err != nil { + return err + } + stmt = strings.ReplaceAll(stmt, "{{.username}}", mig.dbClient.Username()) + + logging.WithFields("migration", mig.String(), "file", migration.Name()).Debug("execute statement") + + _, err = mig.dbClient.ExecContext(ctx, stmt) + if err != nil { + return err + } + } + return nil +} + +func (mig *NewEventsTable) String() string { + return "14_events_push" +} + +func (mig *NewEventsTable) ContinueOnErr(err error) bool { + pgErr := new(pgconn.PgError) + if errors.As(err, &pgErr) { + return pgErr.Code == "42P01" + } + return false +} diff --git a/cmd/setup/14/cockroach/01_disable_inserts.sql b/cmd/setup/14/cockroach/01_disable_inserts.sql new file mode 100644 index 000000000..0f3c277eb --- /dev/null +++ b/cmd/setup/14/cockroach/01_disable_inserts.sql @@ -0,0 +1 @@ +ALTER TABLE eventstore.events RENAME TO events_old; \ No newline at end of file diff --git a/cmd/setup/14/cockroach/02_create_and_fill_events2.sql b/cmd/setup/14/cockroach/02_create_and_fill_events2.sql new file mode 100644 index 000000000..300ac4b62 --- /dev/null +++ b/cmd/setup/14/cockroach/02_create_and_fill_events2.sql @@ -0,0 +1,33 @@ +CREATE TABLE eventstore.events2 ( + instance_id, + aggregate_type, + aggregate_id, + + event_type, + "sequence", + revision, + created_at, + payload, + creator, + "owner", + + "position", + in_tx_order, + + PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence") +) AS SELECT + instance_id, + aggregate_type, + aggregate_id, + + event_type, + event_sequence, + substr(aggregate_version, 2)::SMALLINT, + creation_date, + event_data, + editor_user, + resource_owner, + + creation_date::DECIMAL, + event_sequence +FROM eventstore.events_old; \ No newline at end of file diff --git a/cmd/setup/14/cockroach/03_constraints.sql b/cmd/setup/14/cockroach/03_constraints.sql new file mode 100644 index 000000000..62f119cc4 --- /dev/null +++ b/cmd/setup/14/cockroach/03_constraints.sql @@ -0,0 +1,7 @@ +ALTER TABLE eventstore.events2 ALTER COLUMN event_type SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN revision SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN created_at SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN creator SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN "owner" SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN "position" SET NOT NULL; +ALTER TABLE eventstore.events2 ALTER COLUMN in_tx_order SET NOT NULL; \ No newline at end of file diff --git a/cmd/setup/14/cockroach/04_indexes.sql b/cmd/setup/14/cockroach/04_indexes.sql new file mode 100644 index 000000000..a44265360 --- /dev/null +++ b/cmd/setup/14/cockroach/04_indexes.sql @@ -0,0 +1,3 @@ +CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC) STORING ("position"); +CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type); +CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position"); \ No newline at end of file diff --git a/cmd/setup/14/postgres/01_disable_inserts.sql b/cmd/setup/14/postgres/01_disable_inserts.sql new file mode 100644 index 000000000..0f3c277eb --- /dev/null +++ b/cmd/setup/14/postgres/01_disable_inserts.sql @@ -0,0 +1 @@ +ALTER TABLE eventstore.events RENAME TO events_old; \ No newline at end of file diff --git a/cmd/setup/14/postgres/02_create_and_fill_events2.sql b/cmd/setup/14/postgres/02_create_and_fill_events2.sql new file mode 100644 index 000000000..c33b1f560 --- /dev/null +++ b/cmd/setup/14/postgres/02_create_and_fill_events2.sql @@ -0,0 +1,31 @@ +CREATE TABLE eventstore.events2 ( + instance_id, + aggregate_type, + aggregate_id, + + event_type, + "sequence", + revision, + created_at, + payload, + creator, + "owner", + + "position", + in_tx_order +) AS SELECT + instance_id, + aggregate_type, + aggregate_id, + + event_type, + event_sequence, + substr(aggregate_version, 2)::SMALLINT, + creation_date, + event_data, + editor_user, + resource_owner, + + EXTRACT(EPOCH FROM creation_date), + event_sequence +FROM eventstore.events_old; \ No newline at end of file diff --git a/cmd/setup/14/postgres/03_events2_pk.sql b/cmd/setup/14/postgres/03_events2_pk.sql new file mode 100644 index 000000000..469aae854 --- /dev/null +++ b/cmd/setup/14/postgres/03_events2_pk.sql @@ -0,0 +1,4 @@ +BEGIN; +ALTER TABLE eventstore.events2 DROP CONSTRAINT IF EXISTS events2_pkey; +ALTER TABLE eventstore.events2 ADD PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence"); +COMMIT; \ No newline at end of file diff --git a/cmd/setup/14/postgres/04_constraints.sql b/cmd/setup/14/postgres/04_constraints.sql new file mode 100644 index 000000000..6cccf6b49 --- /dev/null +++ b/cmd/setup/14/postgres/04_constraints.sql @@ -0,0 +1,7 @@ +ALTER TABLE eventstore.events2 ALTER COLUMN event_type SET NOT NULL, + ALTER COLUMN revision SET NOT NULL, + ALTER COLUMN created_at SET NOT NULL, + ALTER COLUMN creator SET NOT NULL, + ALTER COLUMN "owner" SET NOT NULL, + ALTER COLUMN "position" SET NOT NULL, + ALTER COLUMN in_tx_order SET NOT NULL; \ No newline at end of file diff --git a/cmd/setup/14/postgres/05_indexes.sql b/cmd/setup/14/postgres/05_indexes.sql new file mode 100644 index 000000000..36c9bc38a --- /dev/null +++ b/cmd/setup/14/postgres/05_indexes.sql @@ -0,0 +1,3 @@ +CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id); +CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type); +CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position"); \ No newline at end of file diff --git a/cmd/setup/15.go b/cmd/setup/15.go new file mode 100644 index 000000000..174821e0b --- /dev/null +++ b/cmd/setup/15.go @@ -0,0 +1,45 @@ +package setup + +import ( + "context" + "embed" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" +) + +var ( + //go:embed 15/cockroach/*.sql + //go:embed 15/postgres/*.sql + currentProjectionState embed.FS +) + +type CurrentProjectionState struct { + dbClient *database.DB +} + +func (mig *CurrentProjectionState) Execute(ctx context.Context) error { + migrations, err := currentProjectionState.ReadDir("15/" + mig.dbClient.Type()) + if err != nil { + return err + } + for _, migration := range migrations { + stmt, err := readStmt(currentProjectionState, "15", mig.dbClient.Type(), migration.Name()) + if err != nil { + return err + } + + logging.WithFields("file", migration.Name(), "migration", mig.String()).Info("execute statement") + + _, err = mig.dbClient.ExecContext(ctx, stmt) + if err != nil { + return err + } + } + return nil +} + +func (mig *CurrentProjectionState) String() string { + return "15_current_projection_state" +} diff --git a/cmd/setup/15/cockroach/01_new_failed_events.sql b/cmd/setup/15/cockroach/01_new_failed_events.sql new file mode 100644 index 000000000..5fa39c08a --- /dev/null +++ b/cmd/setup/15/cockroach/01_new_failed_events.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS projections.failed_events2 ( + projection_name TEXT NOT NULL + , instance_id TEXT NOT NULL + + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + , event_creation_date TIMESTAMPTZ NOT NULL + , failed_sequence INT8 NOT NULL + + , failure_count INT2 NULL DEFAULT 0 + , error TEXT + , last_failed TIMESTAMPTZ + + , PRIMARY KEY (projection_name, instance_id, aggregate_type, aggregate_id, failed_sequence) +); +CREATE INDEX IF NOT EXISTS fe2_instance_id_idx on projections.failed_events2 (instance_id); \ No newline at end of file diff --git a/cmd/setup/15/cockroach/02_fe_from_projections.sql b/cmd/setup/15/cockroach/02_fe_from_projections.sql new file mode 100644 index 000000000..8bf7a4b8d --- /dev/null +++ b/cmd/setup/15/cockroach/02_fe_from_projections.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.projection_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.error + , fe.last_failed +FROM + projections.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/cockroach/03_fe_from_adminapi.sql b/cmd/setup/15/cockroach/03_fe_from_adminapi.sql new file mode 100644 index 000000000..1616662fe --- /dev/null +++ b/cmd/setup/15/cockroach/03_fe_from_adminapi.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.view_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.err_msg + , fe.last_failed +FROM + adminapi.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/cockroach/04_fe_from_auth.sql b/cmd/setup/15/cockroach/04_fe_from_auth.sql new file mode 100644 index 000000000..a249293e2 --- /dev/null +++ b/cmd/setup/15/cockroach/04_fe_from_auth.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.view_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.err_msg + , fe.last_failed +FROM + auth.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/cockroach/05_current_states.sql b/cmd/setup/15/cockroach/05_current_states.sql new file mode 100644 index 000000000..bc2f5ed77 --- /dev/null +++ b/cmd/setup/15/cockroach/05_current_states.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS projections.current_states ( + projection_name TEXT NOT NULL + , instance_id TEXT NOT NULL + + , last_updated TIMESTAMPTZ + + , aggregate_id TEXT + , aggregate_type TEXT + , "sequence" INT8 + , event_date TIMESTAMPTZ + , "position" DECIMAL + + , PRIMARY KEY (projection_name, instance_id) +); +CREATE INDEX IF NOT EXISTS cs_instance_id_idx ON projections.current_states (instance_id); \ No newline at end of file diff --git a/cmd/setup/15/cockroach/06_cs_from_projections.sql b/cmd/setup/15/cockroach/06_cs_from_projections.sql new file mode 100644 index 000000000..579afb6d4 --- /dev/null +++ b/cmd/setup/15/cockroach/06_cs_from_projections.sql @@ -0,0 +1,29 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) (SELECT + cs.projection_name + , cs.instance_id + , e.created_at + , e.position + , cs.timestamp +FROM + projections.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.aggregate_type = cs.aggregate_type + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + projections.current_sequences cs2 + WHERE + cs.projection_name = cs2.projection_name + AND cs.instance_id = cs2.instance_id + ) +) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/cockroach/07_cs_from_adminapi.sql b/cmd/setup/15/cockroach/07_cs_from_adminapi.sql new file mode 100644 index 000000000..c40d13a06 --- /dev/null +++ b/cmd/setup/15/cockroach/07_cs_from_adminapi.sql @@ -0,0 +1,28 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) (SELECT + cs.view_name + , cs.instance_id + , e.created_at + , e.position + , cs.last_successful_spooler_run +FROM + adminapi.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + adminapi.current_sequences cs2 + WHERE + cs.view_name = cs2.view_name + AND cs.instance_id = cs2.instance_id + ) +) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/cockroach/08_cs_from_auth.sql b/cmd/setup/15/cockroach/08_cs_from_auth.sql new file mode 100644 index 000000000..c8e723610 --- /dev/null +++ b/cmd/setup/15/cockroach/08_cs_from_auth.sql @@ -0,0 +1,28 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) (SELECT + cs.view_name + , cs.instance_id + , e.created_at + , e.position + , cs.last_successful_spooler_run +FROM + auth.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + auth.current_sequences cs2 + WHERE + cs.view_name = cs2.view_name + AND cs.instance_id = cs2.instance_id + ) +) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/01_new_failed_events.sql b/cmd/setup/15/postgres/01_new_failed_events.sql new file mode 100644 index 000000000..5fa39c08a --- /dev/null +++ b/cmd/setup/15/postgres/01_new_failed_events.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS projections.failed_events2 ( + projection_name TEXT NOT NULL + , instance_id TEXT NOT NULL + + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + , event_creation_date TIMESTAMPTZ NOT NULL + , failed_sequence INT8 NOT NULL + + , failure_count INT2 NULL DEFAULT 0 + , error TEXT + , last_failed TIMESTAMPTZ + + , PRIMARY KEY (projection_name, instance_id, aggregate_type, aggregate_id, failed_sequence) +); +CREATE INDEX IF NOT EXISTS fe2_instance_id_idx on projections.failed_events2 (instance_id); \ No newline at end of file diff --git a/cmd/setup/15/postgres/02_fe_from_projections.sql b/cmd/setup/15/postgres/02_fe_from_projections.sql new file mode 100644 index 000000000..e22cc974a --- /dev/null +++ b/cmd/setup/15/postgres/02_fe_from_projections.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.projection_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.error + , fe.last_failed +FROM + projections.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/03_fe_from_adminapi.sql b/cmd/setup/15/postgres/03_fe_from_adminapi.sql new file mode 100644 index 000000000..1616662fe --- /dev/null +++ b/cmd/setup/15/postgres/03_fe_from_adminapi.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.view_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.err_msg + , fe.last_failed +FROM + adminapi.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/04_fe_from_auth.sql b/cmd/setup/15/postgres/04_fe_from_auth.sql new file mode 100644 index 000000000..a249293e2 --- /dev/null +++ b/cmd/setup/15/postgres/04_fe_from_auth.sql @@ -0,0 +1,26 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) SELECT + fe.view_name + , fe.instance_id + , e.aggregate_type + , e.aggregate_id + , e.created_at + , e.sequence + , fe.failure_count + , fe.err_msg + , fe.last_failed +FROM + auth.failed_events fe +JOIN eventstore.events2 e ON + e.instance_id = fe.instance_id + AND e.sequence = fe.failed_sequence +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/05_current_states.sql b/cmd/setup/15/postgres/05_current_states.sql new file mode 100644 index 000000000..bc2f5ed77 --- /dev/null +++ b/cmd/setup/15/postgres/05_current_states.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS projections.current_states ( + projection_name TEXT NOT NULL + , instance_id TEXT NOT NULL + + , last_updated TIMESTAMPTZ + + , aggregate_id TEXT + , aggregate_type TEXT + , "sequence" INT8 + , event_date TIMESTAMPTZ + , "position" DECIMAL + + , PRIMARY KEY (projection_name, instance_id) +); +CREATE INDEX IF NOT EXISTS cs_instance_id_idx ON projections.current_states (instance_id); \ No newline at end of file diff --git a/cmd/setup/15/postgres/06_cs_from_projections.sql b/cmd/setup/15/postgres/06_cs_from_projections.sql new file mode 100644 index 000000000..2cd145c32 --- /dev/null +++ b/cmd/setup/15/postgres/06_cs_from_projections.sql @@ -0,0 +1,28 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) SELECT + cs.projection_name + , cs.instance_id + , e.created_at + , e.position + , cs.timestamp +FROM + projections.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.aggregate_type = cs.aggregate_type + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + projections.current_sequences cs2 + WHERE + cs.projection_name = cs2.projection_name + AND cs.instance_id = cs2.instance_id + ) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/07_cs_from_adminapi.sql b/cmd/setup/15/postgres/07_cs_from_adminapi.sql new file mode 100644 index 000000000..791d0fde9 --- /dev/null +++ b/cmd/setup/15/postgres/07_cs_from_adminapi.sql @@ -0,0 +1,27 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) SELECT + cs.view_name + , cs.instance_id + , e.created_at + , e.position + , cs.last_successful_spooler_run +FROM + adminapi.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + adminapi.current_sequences cs2 + WHERE + cs.view_name = cs2.view_name + AND cs.instance_id = cs2.instance_id + ) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/15/postgres/08_cs_from_auth.sql b/cmd/setup/15/postgres/08_cs_from_auth.sql new file mode 100644 index 000000000..654c7b0bc --- /dev/null +++ b/cmd/setup/15/postgres/08_cs_from_auth.sql @@ -0,0 +1,27 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , event_date + , "position" + , last_updated +) SELECT + cs.view_name + , cs.instance_id + , e.created_at + , e.position + , cs.last_successful_spooler_run +FROM + auth.current_sequences cs +JOIN eventstore.events2 e ON + e.instance_id = cs.instance_id + AND e.sequence = cs.current_sequence + AND cs.current_sequence = ( + SELECT + MAX(cs2.current_sequence) + FROM + auth.current_sequences cs2 + WHERE + cs.view_name = cs2.view_name + AND cs.instance_id = cs2.instance_id + ) +ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/cmd/setup/cleanup.go b/cmd/setup/cleanup.go index 7139b67d3..8da873b40 100644 --- a/cmd/setup/cleanup.go +++ b/cmd/setup/cleanup.go @@ -9,6 +9,8 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" + old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql" + new_es "github.com/zitadel/zitadel/internal/eventstore/v3" "github.com/zitadel/zitadel/internal/migration" ) @@ -29,11 +31,14 @@ func Cleanup(config *Config) { logging.Info("cleanup started") - dbClient, err := database.Connect(config.Database, false) + zitadelDBClient, err := database.Connect(config.Database, false, false) + logging.OnError(err).Fatal("unable to connect to database") + esPusherDBClient, err := database.Connect(config.Database, false, true) logging.OnError(err).Fatal("unable to connect to database") - es, err := eventstore.Start(&eventstore.Config{Client: dbClient}) - logging.OnError(err).Fatal("unable to start eventstore") + config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient) + config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient) + es := eventstore.NewEventstore(config.Eventstore) migration.RegisterMappers(es) step, err := migration.LatestStep(ctx, es) diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 068f524b9..4ff7c0232 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -15,6 +15,7 @@ import ( "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/query/projection" ) @@ -31,6 +32,7 @@ type Config struct { DefaultInstance command.InstanceSetup Machine *id.Config Projections projection.Config + Eventstore *eventstore.Config } func MustNewConfig(v *viper.Viper) *Config { @@ -60,16 +62,15 @@ type Steps struct { s1ProjectionTable *ProjectionTable s2AssetsTable *AssetTable FirstInstance *FirstInstance - s4EventstoreIndexes *EventstoreIndexesNew s5LastFailed *LastFailed s6OwnerRemoveColumns *OwnerRemoveColumns s7LogstoreTables *LogstoreTables s8AuthTokens *AuthTokenIndexes - s9EventstoreIndexes2 *EventstoreIndexesNew CorrectCreationDate *CorrectCreationDate - AddEventCreatedAt *AddEventCreatedAt s12AddOTPColumns *AddOTPColumns s13FixQuotaProjection *FixQuotaConstraints + s14NewEventsTable *NewEventsTable + s15CurrentStates *CurrentProjectionState } type encryptionKeyConfig struct { diff --git a/cmd/setup/eventstore_index.go b/cmd/setup/eventstore_index.go deleted file mode 100644 index 6165d75d2..000000000 --- a/cmd/setup/eventstore_index.go +++ /dev/null @@ -1,29 +0,0 @@ -package setup - -import ( - "context" - "embed" - - "github.com/zitadel/zitadel/internal/database" -) - -type EventstoreIndexesNew struct { - dbClient *database.DB - name string - step string - fileName string - stmts embed.FS -} - -func (mig *EventstoreIndexesNew) Execute(ctx context.Context) error { - stmt, err := readStmt(mig.stmts, mig.step, mig.dbClient.Type(), mig.fileName) - if err != nil { - return err - } - _, err = mig.dbClient.ExecContext(ctx, stmt) - return err -} - -func (mig *EventstoreIndexesNew) String() string { - return mig.name -} diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 1efe70398..8ef9373f7 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -14,6 +14,8 @@ import ( "github.com/zitadel/zitadel/cmd/tls" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" + old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql" + new_es "github.com/zitadel/zitadel/internal/eventstore/v3" "github.com/zitadel/zitadel/internal/migration" "github.com/zitadel/zitadel/internal/query/projection" ) @@ -62,22 +64,26 @@ func Setup(config *Config, steps *Steps, masterKey string) { ctx := context.Background() logging.Info("setup started") - dbClient, err := database.Connect(config.Database, false) + zitadelDBClient, err := database.Connect(config.Database, false, false) + logging.OnError(err).Fatal("unable to connect to database") + esPusherDBClient, err := database.Connect(config.Database, false, true) logging.OnError(err).Fatal("unable to connect to database") - eventstoreClient, err := eventstore.Start(&eventstore.Config{Client: dbClient}) + config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient) + config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient) + eventstoreClient := eventstore.NewEventstore(config.Eventstore) logging.OnError(err).Fatal("unable to start eventstore") migration.RegisterMappers(eventstoreClient) - steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient.DB} - steps.s2AssetsTable = &AssetTable{dbClient: dbClient.DB} + steps.s1ProjectionTable = &ProjectionTable{dbClient: zitadelDBClient.DB} + steps.s2AssetsTable = &AssetTable{dbClient: zitadelDBClient.DB} steps.FirstInstance.instanceSetup = config.DefaultInstance steps.FirstInstance.userEncryptionKey = config.EncryptionKeys.User steps.FirstInstance.smtpEncryptionKey = config.EncryptionKeys.SMTP steps.FirstInstance.oidcEncryptionKey = config.EncryptionKeys.OIDC steps.FirstInstance.masterKey = masterKey - steps.FirstInstance.db = dbClient + steps.FirstInstance.db = zitadelDBClient steps.FirstInstance.es = eventstoreClient steps.FirstInstance.defaults = config.SystemDefaults steps.FirstInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings @@ -85,19 +91,17 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.FirstInstance.externalSecure = config.ExternalSecure steps.FirstInstance.externalPort = config.ExternalPort - steps.s4EventstoreIndexes = New04(dbClient) - steps.s5LastFailed = &LastFailed{dbClient: dbClient.DB} - steps.s6OwnerRemoveColumns = &OwnerRemoveColumns{dbClient: dbClient.DB} - steps.s7LogstoreTables = &LogstoreTables{dbClient: dbClient.DB, username: config.Database.Username(), dbType: config.Database.Type()} - steps.s8AuthTokens = &AuthTokenIndexes{dbClient: dbClient} - steps.s9EventstoreIndexes2 = New09(dbClient) - steps.CorrectCreationDate.dbClient = dbClient - steps.AddEventCreatedAt.dbClient = dbClient - steps.AddEventCreatedAt.step10 = steps.CorrectCreationDate - steps.s12AddOTPColumns = &AddOTPColumns{dbClient: dbClient} - steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: dbClient} + steps.s5LastFailed = &LastFailed{dbClient: zitadelDBClient.DB} + steps.s6OwnerRemoveColumns = &OwnerRemoveColumns{dbClient: zitadelDBClient.DB} + steps.s7LogstoreTables = &LogstoreTables{dbClient: zitadelDBClient.DB, username: config.Database.Username(), dbType: config.Database.Type()} + steps.s8AuthTokens = &AuthTokenIndexes{dbClient: zitadelDBClient} + steps.CorrectCreationDate.dbClient = esPusherDBClient + steps.s12AddOTPColumns = &AddOTPColumns{dbClient: zitadelDBClient} + steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: zitadelDBClient} + steps.s14NewEventsTable = &NewEventsTable{dbClient: esPusherDBClient} + steps.s15CurrentStates = &CurrentProjectionState{dbClient: zitadelDBClient} - err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil) + err = projection.Create(ctx, zitadelDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") repeatableSteps := []migration.RepeatableMigration{ @@ -114,32 +118,28 @@ func Setup(config *Config, steps *Steps, masterKey string) { }, } + err = migration.Migrate(ctx, eventstoreClient, steps.s14NewEventsTable) + logging.WithFields("name", steps.s14NewEventsTable.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable) - logging.OnError(err).Fatal("unable to migrate step 1") + logging.WithFields("name", steps.s1ProjectionTable.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable) - logging.OnError(err).Fatal("unable to migrate step 2") + logging.WithFields("name", steps.s2AssetsTable.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.FirstInstance) - logging.OnError(err).Fatal("unable to migrate step 3") - err = migration.Migrate(ctx, eventstoreClient, steps.s4EventstoreIndexes) - logging.OnError(err).Fatal("unable to migrate step 4") + logging.WithFields("name", steps.FirstInstance.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s5LastFailed) - logging.OnError(err).Fatal("unable to migrate step 5") + logging.WithFields("name", steps.s5LastFailed.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s6OwnerRemoveColumns) - logging.OnError(err).Fatal("unable to migrate step 6") + logging.WithFields("name", steps.s6OwnerRemoveColumns.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s7LogstoreTables) - logging.OnError(err).Fatal("unable to migrate step 7") + logging.WithFields("name", steps.s7LogstoreTables.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s8AuthTokens) - logging.OnError(err).Fatal("unable to migrate step 8") - err = migration.Migrate(ctx, eventstoreClient, steps.s9EventstoreIndexes2) - logging.OnError(err).Fatal("unable to migrate step 9") - err = migration.Migrate(ctx, eventstoreClient, steps.CorrectCreationDate) - logging.OnError(err).Fatal("unable to migrate step 10") - err = migration.Migrate(ctx, eventstoreClient, steps.AddEventCreatedAt) - logging.OnError(err).Fatal("unable to migrate step 11") + logging.WithFields("name", steps.s8AuthTokens.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s12AddOTPColumns) - logging.OnError(err).Fatal("unable to migrate step 12") + logging.WithFields("name", steps.s12AddOTPColumns.String()).OnError(err).Fatal("migration failed") err = migration.Migrate(ctx, eventstoreClient, steps.s13FixQuotaProjection) - logging.OnError(err).Fatal("unable to migrate step 13") + logging.WithFields("name", steps.s13FixQuotaProjection.String()).OnError(err).Fatal("migration failed") + err = migration.Migrate(ctx, eventstoreClient, steps.s15CurrentStates) + logging.WithFields("name", steps.s15CurrentStates.String()).OnError(err).Fatal("migration failed") for _, repeatableStep := range repeatableSteps { err = migration.Migrate(ctx, eventstoreClient, repeatableStep) diff --git a/cmd/start/start.go b/cmd/start/start.go index 6a017af0d..dfc759e3a 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -60,6 +60,8 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql" + new_es "github.com/zitadel/zitadel/internal/eventstore/v3" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/logstore" "github.com/zitadel/zitadel/internal/logstore/emitters/access" @@ -121,12 +123,16 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error ctx := context.Background() - dbClient, err := database.Connect(config.Database, false) + zitadelDBClient, err := database.Connect(config.Database, false, false) + if err != nil { + return fmt.Errorf("cannot start client for projection: %w", err) + } + esPusherDBClient, err := database.Connect(config.Database, false, true) if err != nil { return fmt.Errorf("cannot start client for projection: %w", err) } - keyStorage, err := cryptoDB.NewKeyStorage(dbClient, masterKey) + keyStorage, err := cryptoDB.NewKeyStorage(zitadelDBClient, masterKey) if err != nil { return fmt.Errorf("cannot start key storage: %w", err) } @@ -135,18 +141,16 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error return err } - config.Eventstore.Client = dbClient - eventstoreClient, err := eventstore.Start(config.Eventstore) - if err != nil { - return fmt.Errorf("cannot start eventstore for queries: %w", err) - } + config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient) + config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient) + eventstoreClient := eventstore.NewEventstore(config.Eventstore) sessionTokenVerifier := internal_authz.SessionTokenVerifier(keys.OIDC) queries, err := query.StartQueries( ctx, eventstoreClient, - dbClient, + zitadelDBClient, config.Projections, config.SystemDefaults, keys.IDPConfig, @@ -160,12 +164,13 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID) } }, + config.SystemAPIUsers, ) if err != nil { return fmt.Errorf("cannot start queries: %w", err) } - authZRepo, err := authz.Start(queries, dbClient, keys.OIDC, config.ExternalSecure, config.Eventstore.AllowOrderByCreationDate) + authZRepo, err := authz.Start(queries, eventstoreClient, zitadelDBClient, keys.OIDC, config.ExternalSecure) if err != nil { return fmt.Errorf("error starting authz repo: %w", err) } @@ -173,7 +178,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error return internal_authz.CheckPermission(ctx, authZRepo, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID) } - storage, err := config.AssetStorage.NewStorage(dbClient.DB) + storage, err := config.AssetStorage.NewStorage(zitadelDBClient.DB) if err != nil { return fmt.Errorf("cannot start asset storage client: %w", err) } @@ -215,7 +220,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error if err != nil { return err } - actionsExecutionDBEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, config.Quotas.Execution, execution.NewDatabaseLogStorage(dbClient, commands, queries)) + actionsExecutionDBEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, config.Quotas.Execution, execution.NewDatabaseLogStorage(zitadelDBClient, commands, queries)) if err != nil { return err } @@ -254,7 +259,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error commands, queries, eventstoreClient, - dbClient, + zitadelDBClient, config, storage, authZRepo, @@ -271,7 +276,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error if server != nil { server <- &Server{ Config: config, - DB: dbClient, + DB: zitadelDBClient, KeyStorage: keyStorage, Keys: keys, Eventstore: eventstoreClient, @@ -338,18 +343,25 @@ func startAPIs( if err != nil { return fmt.Errorf("error creating api %w", err) } - authRepo, err := auth_es.Start(ctx, config.Auth, config.SystemDefaults, commands, queries, dbClient, eventstore, keys.OIDC, keys.User, config.Eventstore.AllowOrderByCreationDate) + + config.Auth.Spooler.Client = dbClient + config.Auth.Spooler.Eventstore = eventstore + authRepo, err := auth_es.Start(ctx, config.Auth, config.SystemDefaults, commands, queries, dbClient, eventstore, keys.OIDC, keys.User) if err != nil { return fmt.Errorf("error starting auth repo: %w", err) } - adminRepo, err := admin_es.Start(ctx, config.Admin, store, dbClient, eventstore, config.Eventstore.AllowOrderByCreationDate) + + config.Admin.Spooler.Client = dbClient + config.Admin.Spooler.Eventstore = eventstore + err = admin_es.Start(ctx, config.Admin, store, dbClient) if err != nil { return fmt.Errorf("error starting admin repo: %w", err) } - if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.DatabaseName(), config.DefaultInstance, config.ExternalDomain)); err != nil { + + if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, config.Database.DatabaseName(), config.DefaultInstance, config.ExternalDomain)); err != nil { return err } - if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.DatabaseName(), commands, queries, config.SystemDefaults, adminRepo, config.ExternalSecure, keys.User, config.AuditLogRetention)); err != nil { + if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.DatabaseName(), commands, queries, config.SystemDefaults, config.ExternalSecure, keys.User, config.AuditLogRetention)); err != nil { return err } if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil { diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 7929e62bc..0b173bdaa 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -306,6 +306,7 @@ export interface OnboardingActions { iconClasses?: string; darkcolor: string; lightcolor: string; + aggregateType: string; } type OnboardingEvent = { @@ -330,7 +331,12 @@ export class AdminService { tap(() => this.onboardingLoading.next(true)), switchMap((actions) => { const searchForTypes = actions.map((oe) => oe.oneof).flat(); - const eventsReq = new ListEventsRequest().setAsc(true).setEventTypesList(searchForTypes).setAsc(false); + const aggregateTypes = actions.map((oe) => oe.aggregateType); + const eventsReq = new ListEventsRequest() + .setAsc(true) + .setEventTypesList(searchForTypes) + .setAggregateTypesList(aggregateTypes) + .setAsc(false); return from(this.listEvents(eventsReq)).pipe( map((events) => { const el = events.toObject().eventsList.filter((e) => e.editor?.service !== 'System-API' && e.editor?.userId); diff --git a/console/src/app/utils/onboarding.ts b/console/src/app/utils/onboarding.ts index 97f2de8c5..f93c11db1 100644 --- a/console/src/app/utils/onboarding.ts +++ b/console/src/app/utils/onboarding.ts @@ -28,6 +28,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'las la-database', darkcolor: greendark, lightcolor: greenlight, + aggregateType: 'project', }, { order: 1, @@ -37,6 +38,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'lab la-openid', darkcolor: purpledark, lightcolor: purplelight, + aggregateType: 'project', }, { order: 2, @@ -46,6 +48,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'las la-user', darkcolor: bluedark, lightcolor: bluelight, + aggregateType: 'user', }, { order: 3, @@ -55,6 +58,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'las la-shield-alt', darkcolor: reddark, lightcolor: redlight, + aggregateType: 'user_grant', }, { order: 4, @@ -65,6 +69,7 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'las la-swatchbook', darkcolor: pinkdark, lightcolor: pinklight, + aggregateType: 'instance', }, { order: 5, @@ -75,5 +80,6 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ iconClasses: 'las la-envelope', darkcolor: yellowdark, lightcolor: yellowlight, + aggregateType: 'instance', }, ]; diff --git a/e2e/config/host.docker.internal/zitadel.yaml b/e2e/config/host.docker.internal/zitadel.yaml index ff3ecc76e..fdc55266c 100644 --- a/e2e/config/host.docker.internal/zitadel.yaml +++ b/e2e/config/host.docker.internal/zitadel.yaml @@ -8,6 +8,9 @@ Database: cockroach: # This makes the e2e config reusable with an out-of-docker zitadel process and an /etc/hosts entry Host: host.docker.internal + EventPushConnRatio: 0.2 + MaxOpenConns: 40 + MaxIdleConns: 20 TLS: Enabled: false diff --git a/e2e/config/localhost/docker-compose.yaml b/e2e/config/localhost/docker-compose.yaml index 90421e1c1..5dc5578a6 100644 --- a/e2e/config/localhost/docker-compose.yaml +++ b/e2e/config/localhost/docker-compose.yaml @@ -32,7 +32,7 @@ services: db: restart: 'always' - image: 'cockroachdb/cockroach:v22.2.2' + image: 'cockroachdb/cockroach:v22.2.10' command: 'start-single-node --insecure --http-addr :9090' healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:9090/health?ready=1'] diff --git a/e2e/config/localhost/zitadel.yaml b/e2e/config/localhost/zitadel.yaml index 1c560ddfd..cfafaa637 100644 --- a/e2e/config/localhost/zitadel.yaml +++ b/e2e/config/localhost/zitadel.yaml @@ -8,6 +8,9 @@ Database: cockroach: # This makes the e2e config reusable with an out-of-docker zitadel process and an /etc/hosts entry Host: host.docker.internal + EventPushConnRatio: 0.2 + MaxOpenConns: 40 + MaxIdleConns: 20 TLS: Enabled: false diff --git a/internal/actions/object/metadata.go b/internal/actions/object/metadata.go index 25c4857bd..61311fe50 100644 --- a/internal/actions/object/metadata.go +++ b/internal/actions/object/metadata.go @@ -16,7 +16,7 @@ func UserMetadataListFromQuery(c *actions.FieldConfig, metadata *query.UserMetad result := &userMetadataList{ Count: metadata.Count, Sequence: metadata.Sequence, - Timestamp: metadata.Timestamp, + Timestamp: metadata.LastRun, Metadata: make([]*userMetadata, len(metadata.Metadata)), } diff --git a/internal/actions/object/user.go b/internal/actions/object/user.go index 860185828..cf403ac59 100644 --- a/internal/actions/object/user.go +++ b/internal/actions/object/user.go @@ -143,7 +143,7 @@ type humanUser struct { Sequence uint64 State domain.UserState Username string - LoginNames database.StringArray + LoginNames database.TextArray[string] PreferredLoginName string Human human } @@ -170,7 +170,7 @@ type machineUser struct { Sequence uint64 State domain.UserState Username string - LoginNames database.StringArray + LoginNames database.TextArray[string] PreferredLoginName string Machine machine } diff --git a/internal/actions/object/user_grant.go b/internal/actions/object/user_grant.go index 204a4d834..73de8bd0a 100644 --- a/internal/actions/object/user_grant.go +++ b/internal/actions/object/user_grant.go @@ -65,7 +65,7 @@ func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) g grantList := &userGrantList{ Count: userGrants.Count, Sequence: userGrants.Sequence, - Timestamp: userGrants.Timestamp, + Timestamp: userGrants.LastRun, Grants: make([]*userGrant, len(userGrants.UserGrants)), } diff --git a/internal/admin/repository/administrator.go b/internal/admin/repository/administrator.go deleted file mode 100644 index 87e02ee6d..000000000 --- a/internal/admin/repository/administrator.go +++ /dev/null @@ -1,14 +0,0 @@ -package repository - -import ( - "context" - - "github.com/zitadel/zitadel/internal/view/model" -) - -type AdministratorRepository interface { - GetFailedEvents(ctx context.Context, instanceID string) ([]*model.FailedEvent, error) - RemoveFailedEvent(context.Context, *model.FailedEvent) error - GetViews(instanceID string) ([]*model.View, error) - ClearView(ctx context.Context, db, viewName string) error -} diff --git a/internal/admin/repository/eventsourcing/eventstore/administrator.go b/internal/admin/repository/eventsourcing/eventstore/administrator.go deleted file mode 100644 index 0272a7d3c..000000000 --- a/internal/admin/repository/eventsourcing/eventstore/administrator.go +++ /dev/null @@ -1,51 +0,0 @@ -package eventstore - -import ( - "context" - - "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view" - view_model "github.com/zitadel/zitadel/internal/view/model" - "github.com/zitadel/zitadel/internal/view/repository" -) - -var dbList = []string{"auth", "adminapi"} - -type AdministratorRepo struct { - View *view.View -} - -func (repo *AdministratorRepo) GetFailedEvents(ctx context.Context, instanceID string) ([]*view_model.FailedEvent, error) { - allFailedEvents := make([]*view_model.FailedEvent, 0) - for _, db := range dbList { - failedEvents, err := repo.View.AllFailedEvents(db, instanceID) - if err != nil { - return nil, err - } - for _, failedEvent := range failedEvents { - allFailedEvents = append(allFailedEvents, repository.FailedEventToModel(failedEvent)) - } - } - return allFailedEvents, nil -} - -func (repo *AdministratorRepo) RemoveFailedEvent(ctx context.Context, failedEvent *view_model.FailedEvent) error { - return repo.View.RemoveFailedEvent(failedEvent.Database, repository.FailedEventFromModel(failedEvent)) -} - -func (repo *AdministratorRepo) GetViews(instanceID string) ([]*view_model.View, error) { - views := make([]*view_model.View, 0) - for _, db := range dbList { - sequences, err := repo.View.AllCurrentSequences(db, instanceID) - if err != nil { - return nil, err - } - for _, sequence := range sequences { - views = append(views, repository.CurrentSequenceToModel(sequence)) - } - } - return views, nil -} - -func (repo *AdministratorRepo) ClearView(ctx context.Context, database, view string) error { - return repo.View.ClearView(database, view) -} diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index e95e0232e..a1ca9cb43 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -5,56 +5,55 @@ import ( "time" "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" + handler2 "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/static" ) -type Configs map[string]*Config - type Config struct { + Client *database.DB + Eventstore *eventstore.Eventstore + + BulkLimit uint64 + FailureCountUntilSkip uint64 + HandleActiveInstances time.Duration + TransactionDuration time.Duration + Handlers map[string]*ConfigOverwrites +} + +type ConfigOverwrites struct { MinimumCycleDuration time.Duration } -type handler struct { - view *view.View - bulkLimit uint64 - cycleDuration time.Duration - errorCountUntilSkip uint64 - - es v1.Eventstore -} - -func (h *handler) Eventstore() v1.Eventstore { - return h.es -} - -func Register(ctx context.Context, configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, static static.Storage) []query.Handler { - handlers := []query.Handler{} - if static != nil { - handlers = append(handlers, newStyling(ctx, - handler{view, bulkLimit, configs.cycleDuration("Styling"), errorCount, es}, - static)) +func Register(ctx context.Context, config Config, view *view.View, static static.Storage) { + if static == nil { + return } - return handlers + + newStyling(ctx, + config.overwrite("Styling"), + static, + view, + ).Start(ctx) } -func (configs Configs) cycleDuration(viewModel string) time.Duration { - c, ok := configs[viewModel] +func (config Config) overwrite(viewModel string) handler2.Config { + c := handler2.Config{ + Client: config.Client, + Eventstore: config.Eventstore, + BulkLimit: uint16(config.BulkLimit), + RequeueEvery: 3 * time.Minute, + HandleActiveInstances: config.HandleActiveInstances, + MaxFailureCount: uint8(config.FailureCountUntilSkip), + TransactionDuration: config.TransactionDuration, + } + overwrite, ok := config.Handlers[viewModel] if !ok { - return 3 * time.Minute + return c } - return c.MinimumCycleDuration -} - -func (h *handler) MinimumCycleDuration() time.Duration { - return h.cycleDuration -} - -func (h *handler) LockDuration() time.Duration { - return h.cycleDuration / 3 -} - -func (h *handler) QueryLimit() uint64 { - return h.bulkLimit + if overwrite.MinimumCycleDuration > 0 { + c.RequeueEvery = overwrite.MinimumCycleDuration + } + return c } diff --git a/internal/admin/repository/eventsourcing/handler/styling.go b/internal/admin/repository/eventsourcing/handler/styling.go index ed96aa463..2f8964b51 100644 --- a/internal/admin/repository/eventsourcing/handler/styling.go +++ b/internal/admin/repository/eventsourcing/handler/styling.go @@ -9,15 +9,12 @@ import ( "github.com/lucasb-eyer/go-colorful" "github.com/muesli/gamut" - "github.com/zitadel/logging" + admin_view "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/api/ui/login" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" iam_model "github.com/zitadel/zitadel/internal/iam/repository/view/model" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -28,152 +25,231 @@ const ( stylingTable = "adminapi.styling2" ) +var _ handler.Projection = (*Styling)(nil) + type Styling struct { - handler - static static.Storage - subscription *v1.Subscription + static static.Storage + view *admin_view.View } -func newStyling(ctx context.Context, handler handler, static static.Storage) *Styling { - h := &Styling{ - handler: handler, - static: static, - } - h.subscribe(ctx) - - return h +func newStyling(ctx context.Context, config handler.Config, static static.Storage, view *admin_view.View) *handler.Handler { + return handler.NewHandler( + ctx, + &config, + &Styling{ + static: static, + view: view, + }, + ) } -func (m *Styling) subscribe(ctx context.Context) { - m.subscription = m.es.Subscribe(m.AggregateTypes()...) - go func() { - for event := range m.subscription.Events { - query.ReduceEvent(ctx, m, event) - } - }() -} - -func (m *Styling) ViewModel() string { +// Name implements [handler.Projection] +func (*Styling) Name() string { return stylingTable } -func (m *Styling) Subscription() *v1.Subscription { - return m.subscription -} - -func (_ *Styling) AggregateTypes() []models.AggregateType { - return []models.AggregateType{org.AggregateType, instance.AggregateType} -} - -func (m *Styling) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - sequence, err := m.view.GetLatestStylingSequence(ctx, instanceID) - if err != nil { - return 0, err +// Reducers implements [handler.Projection] +func (s *Styling) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.LabelPolicyAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyChangedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyLogoAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyLogoRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyIconAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyIconRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyLogoDarkAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyLogoDarkRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyIconDarkAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyIconDarkRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyFontAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyFontRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyAssetsRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.LabelPolicyActivatedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: org.OrgRemovedEventType, + Reduce: s.processLabelPolicy, + }, + }, + }, + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.LabelPolicyAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyChangedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyLogoAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyLogoRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyIconAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyIconRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyLogoDarkAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyLogoDarkRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyIconDarkAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyIconDarkRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyFontAddedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyFontRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyAssetsRemovedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.LabelPolicyActivatedEventType, + Reduce: s.processLabelPolicy, + }, + { + Event: instance.InstanceRemovedEventType, + Reduce: s.processLabelPolicy, + }, + }, + }, } - return sequence.CurrentSequence, nil } -func (m *Styling) EventQuery(ctx context.Context, instanceIDs []string) (*models.SearchQuery, error) { - sequences, err := m.view.GetLatestStylingSequences(ctx, instanceIDs) - if err != nil { - return nil, err - } - searchQuery := models.NewSearchQuery() - for _, instanceID := range instanceIDs { - var seq uint64 - for _, sequence := range sequences { - if sequence.InstanceID == instanceID { - seq = sequence.CurrentSequence - break +func (m *Styling) processLabelPolicy(event eventstore.Event) (_ *handler.Statement, err error) { + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + policy := new(iam_model.LabelPolicyView) + switch event.Type() { + case instance.LabelPolicyAddedEventType, + org.LabelPolicyAddedEventType: + err = policy.AppendEvent(event) + case instance.LabelPolicyChangedEventType, + org.LabelPolicyChangedEventType, + instance.LabelPolicyLogoAddedEventType, + org.LabelPolicyLogoAddedEventType, + instance.LabelPolicyLogoRemovedEventType, + org.LabelPolicyLogoRemovedEventType, + instance.LabelPolicyIconAddedEventType, + org.LabelPolicyIconAddedEventType, + instance.LabelPolicyIconRemovedEventType, + org.LabelPolicyIconRemovedEventType, + instance.LabelPolicyLogoDarkAddedEventType, + org.LabelPolicyLogoDarkAddedEventType, + instance.LabelPolicyLogoDarkRemovedEventType, + org.LabelPolicyLogoDarkRemovedEventType, + instance.LabelPolicyIconDarkAddedEventType, + org.LabelPolicyIconDarkAddedEventType, + instance.LabelPolicyIconDarkRemovedEventType, + org.LabelPolicyIconDarkRemovedEventType, + instance.LabelPolicyFontAddedEventType, + org.LabelPolicyFontAddedEventType, + instance.LabelPolicyFontRemovedEventType, + org.LabelPolicyFontRemovedEventType, + instance.LabelPolicyAssetsRemovedEventType, + org.LabelPolicyAssetsRemovedEventType: + + policy, err = m.view.StylingByAggregateIDAndState(event.Aggregate().ID, event.Aggregate().InstanceID, int32(domain.LabelPolicyStatePreview)) + if err != nil { + return err } + err = policy.AppendEvent(event) + case instance.LabelPolicyActivatedEventType, + org.LabelPolicyActivatedEventType: + + policy, err = m.view.StylingByAggregateIDAndState(event.Aggregate().ID, event.Aggregate().InstanceID, int32(domain.LabelPolicyStatePreview)) + if err != nil { + return err + } + err = policy.AppendEvent(event) + if err != nil { + return err + } + err = m.generateStylingFile(policy) + case instance.InstanceRemovedEventType: + err = m.deleteInstanceFilesFromStorage(event.Aggregate().InstanceID) + if err != nil { + return err + } + return m.view.DeleteInstanceStyling(event) + case org.OrgRemovedEventType: + return m.view.UpdateOrgOwnerRemovedStyling(event) + default: + return nil } - searchQuery.AddQuery(). - AggregateTypeFilter(m.AggregateTypes()...). - LatestSequenceFilter(seq). - InstanceIDFilter(instanceID) - } - return searchQuery, nil -} - -func (m *Styling) Reduce(event *models.Event) (err error) { - switch event.AggregateType { - case org.AggregateType, instance.AggregateType: - err = m.processLabelPolicy(event) - } - return err -} - -func (m *Styling) processLabelPolicy(event *models.Event) (err error) { - policy := new(iam_model.LabelPolicyView) - switch eventstore.EventType(event.Type) { - case instance.LabelPolicyAddedEventType, - org.LabelPolicyAddedEventType: - err = policy.AppendEvent(event) - case instance.LabelPolicyChangedEventType, - org.LabelPolicyChangedEventType, - instance.LabelPolicyLogoAddedEventType, - org.LabelPolicyLogoAddedEventType, - instance.LabelPolicyLogoRemovedEventType, - org.LabelPolicyLogoRemovedEventType, - instance.LabelPolicyIconAddedEventType, - org.LabelPolicyIconAddedEventType, - instance.LabelPolicyIconRemovedEventType, - org.LabelPolicyIconRemovedEventType, - instance.LabelPolicyLogoDarkAddedEventType, - org.LabelPolicyLogoDarkAddedEventType, - instance.LabelPolicyLogoDarkRemovedEventType, - org.LabelPolicyLogoDarkRemovedEventType, - instance.LabelPolicyIconDarkAddedEventType, - org.LabelPolicyIconDarkAddedEventType, - instance.LabelPolicyIconDarkRemovedEventType, - org.LabelPolicyIconDarkRemovedEventType, - instance.LabelPolicyFontAddedEventType, - org.LabelPolicyFontAddedEventType, - instance.LabelPolicyFontRemovedEventType, - org.LabelPolicyFontRemovedEventType, - instance.LabelPolicyAssetsRemovedEventType, - org.LabelPolicyAssetsRemovedEventType: - policy, err = m.view.StylingByAggregateIDAndState(event.AggregateID, event.InstanceID, int32(domain.LabelPolicyStatePreview)) if err != nil { return err } - err = policy.AppendEvent(event) - case instance.LabelPolicyActivatedEventType, - org.LabelPolicyActivatedEventType: - policy, err = m.view.StylingByAggregateIDAndState(event.AggregateID, event.InstanceID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - err = policy.AppendEvent(event) - if err != nil { - return err - } - err = m.generateStylingFile(policy) - case instance.InstanceRemovedEventType: - err = m.deleteInstanceFilesFromStorage(event.InstanceID) - if err != nil { - return err - } - return m.view.DeleteInstanceStyling(event) - case org.OrgRemovedEventType: - return m.view.UpdateOrgOwnerRemovedStyling(event) - default: - return m.view.ProcessedStylingSequence(event) - } - if err != nil { - return err - } - return m.view.PutStyling(policy, event) -} - -func (m *Styling) OnError(event *models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") - return spooler.HandleError(event, err, m.view.GetLatestStylingFailedEvent, m.view.ProcessedStylingFailedEvent, m.view.ProcessedStylingSequence, m.errorCountUntilSkip) -} - -func (m *Styling) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(m.view.UpdateStylingSpoolerRunTimestamp, instanceIDs) + return m.view.PutStyling(policy, event) + }), nil } func (m *Styling) generateStylingFile(policy *iam_model.LabelPolicyView) error { diff --git a/internal/admin/repository/eventsourcing/repository.go b/internal/admin/repository/eventsourcing/repository.go index febc2629d..fadab8864 100644 --- a/internal/admin/repository/eventsourcing/repository.go +++ b/internal/admin/repository/eventsourcing/repository.go @@ -3,46 +3,23 @@ package eventsourcing import ( "context" - "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/eventstore" - "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/spooler" + admin_handler "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/handler" admin_view "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/database" - eventstore2 "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - es_spol "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" "github.com/zitadel/zitadel/internal/static" ) type Config struct { - SearchLimit uint64 - Spooler spooler.SpoolerConfig + Spooler admin_handler.Config } -type EsRepository struct { - spooler *es_spol.Spooler - eventstore.AdministratorRepo -} - -func Start(ctx context.Context, conf Config, static static.Storage, dbClient *database.DB, esV2 *eventstore2.Eventstore, allowOrderByCreationDate bool) (*EsRepository, error) { - es, err := v1.Start(dbClient, allowOrderByCreationDate) - if err != nil { - return nil, err - } +func Start(ctx context.Context, conf Config, static static.Storage, dbClient *database.DB) error { view, err := admin_view.StartView(dbClient) if err != nil { - return nil, err + return err } - spool := spooler.StartSpooler(ctx, conf.Spooler, es, esV2, view, dbClient, static) + admin_handler.Register(ctx, conf.Spooler, view, static) - return &EsRepository{ - spooler: spool, - AdministratorRepo: eventstore.AdministratorRepo{ - View: view, - }, - }, nil -} - -func (repo *EsRepository) Health(ctx context.Context) error { return nil } diff --git a/internal/admin/repository/eventsourcing/spooler/lock.go b/internal/admin/repository/eventsourcing/spooler/lock.go deleted file mode 100644 index 7da3ea937..000000000 --- a/internal/admin/repository/eventsourcing/spooler/lock.go +++ /dev/null @@ -1,20 +0,0 @@ -package spooler - -import ( - "database/sql" - "time" - - es_locker "github.com/zitadel/zitadel/internal/eventstore/v1/locker" -) - -const ( - lockTable = "adminapi.locks" -) - -type locker struct { - dbClient *sql.DB -} - -func (l *locker) Renew(lockerID, viewModel, instanceID string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, instanceID, waitTime) -} diff --git a/internal/admin/repository/eventsourcing/spooler/spooler.go b/internal/admin/repository/eventsourcing/spooler/spooler.go deleted file mode 100644 index 8b9fa592d..000000000 --- a/internal/admin/repository/eventsourcing/spooler/spooler.go +++ /dev/null @@ -1,35 +0,0 @@ -package spooler - -import ( - "context" - - "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/handler" - "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing/view" - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" - "github.com/zitadel/zitadel/internal/static" -) - -type SpoolerConfig struct { - BulkLimit uint64 - FailureCountUntilSkip uint64 - ConcurrentWorkers int - ConcurrentInstances int - Handlers handler.Configs -} - -func StartSpooler(ctx context.Context, c SpoolerConfig, es v1.Eventstore, esV2 *eventstore.Eventstore, view *view.View, sql *database.DB, static static.Storage) *spooler.Spooler { - spoolerConfig := spooler.Config{ - Eventstore: es, - EventstoreV2: esV2, - Locker: &locker{dbClient: sql.DB}, - ConcurrentWorkers: c.ConcurrentWorkers, - ConcurrentInstances: c.ConcurrentInstances, - ViewHandlers: handler.Register(ctx, c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, static), - } - spool := spoolerConfig.New() - spool.Start() - return spool -} diff --git a/internal/admin/repository/eventsourcing/view/error_event.go b/internal/admin/repository/eventsourcing/view/error_event.go deleted file mode 100644 index 3b4711963..000000000 --- a/internal/admin/repository/eventsourcing/view/error_event.go +++ /dev/null @@ -1,26 +0,0 @@ -package view - -import ( - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - errTable = "adminapi.failed_events" - errColumn = "failed_events" -) - -func (v *View) saveFailedEvent(failedEvent *repository.FailedEvent) error { - return repository.SaveFailedEvent(v.Db, errTable, failedEvent) -} - -func (v *View) RemoveFailedEvent(database string, failedEvent *repository.FailedEvent) error { - return repository.RemoveFailedEvent(v.Db, database+"."+errColumn, failedEvent) -} - -func (v *View) latestFailedEvent(viewName, instanceID string, sequence uint64) (*repository.FailedEvent, error) { - return repository.LatestFailedEvent(v.Db, errTable, viewName, instanceID, sequence) -} - -func (v *View) AllFailedEvents(db, instanceID string) ([]*repository.FailedEvent, error) { - return repository.AllFailedEvents(v.Db, db+"."+errColumn, instanceID) -} diff --git a/internal/admin/repository/eventsourcing/view/sequence.go b/internal/admin/repository/eventsourcing/view/sequence.go deleted file mode 100644 index 8de577f89..000000000 --- a/internal/admin/repository/eventsourcing/view/sequence.go +++ /dev/null @@ -1,49 +0,0 @@ -package view - -import ( - "context" - "time" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - sequencesTable = "adminapi.current_sequences" -) - -func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, event.InstanceID, event.Sequence, event.CreationDate) -} - -func (v *View) latestSequence(ctx context.Context, viewName, instanceID string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, v.TimeTravel(ctx, sequencesTable), viewName, instanceID) -} - -func (v *View) latestSequences(ctx context.Context, viewName string, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return repository.LatestSequences(v.Db, v.TimeTravel(ctx, sequencesTable), viewName, instanceIDs) -} - -func (v *View) AllCurrentSequences(db, instanceID string) ([]*repository.CurrentSequence, error) { - return repository.AllCurrentSequences(v.Db, db+".current_sequences", instanceID) -} - -func (v *View) updateSpoolerRunSequence(viewName string, instanceIDs []string) error { - currentSequences, err := repository.LatestSequences(v.Db, sequencesTable, viewName, instanceIDs) - if err != nil { - return err - } - for _, currentSequence := range currentSequences { - if currentSequence.ViewName == "" { - currentSequence.ViewName = viewName - } - currentSequence.LastSuccessfulSpoolerRun = time.Now() - } - return repository.UpdateCurrentSequences(v.Db, sequencesTable, currentSequences) -} - -func (v *View) ClearView(db, viewName string) error { - truncateView := db + "." + viewName - sequenceTable := db + ".current_sequences" - return repository.ClearView(v.Db, truncateView, sequenceTable) -} diff --git a/internal/admin/repository/eventsourcing/view/styling.go b/internal/admin/repository/eventsourcing/view/styling.go index ac477b5d9..8ee15a194 100644 --- a/internal/admin/repository/eventsourcing/view/styling.go +++ b/internal/admin/repository/eventsourcing/view/styling.go @@ -1,12 +1,9 @@ package view import ( - "context" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/iam/repository/view" "github.com/zitadel/zitadel/internal/iam/repository/view/model" - global_view "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -17,50 +14,14 @@ func (v *View) StylingByAggregateIDAndState(aggregateID, instanceID string, stat return view.GetStylingByAggregateIDAndState(v.Db, stylingTyble, aggregateID, instanceID, state) } -func (v *View) PutStyling(policy *model.LabelPolicyView, event *models.Event) error { - err := view.PutStyling(v.Db, stylingTyble, policy) - if err != nil { - return err - } - return v.ProcessedStylingSequence(event) +func (v *View) PutStyling(policy *model.LabelPolicyView, event eventstore.Event) error { + return view.PutStyling(v.Db, stylingTyble, policy) } -func (v *View) DeleteInstanceStyling(event *models.Event) error { - err := view.DeleteInstanceStyling(v.Db, stylingTyble, event.InstanceID) - if err != nil { - return err - } - return v.ProcessedStylingSequence(event) +func (v *View) DeleteInstanceStyling(event eventstore.Event) error { + return view.DeleteInstanceStyling(v.Db, stylingTyble, event.Aggregate().InstanceID) } -func (v *View) UpdateOrgOwnerRemovedStyling(event *models.Event) error { - err := view.UpdateOrgOwnerRemovedStyling(v.Db, stylingTyble, event.InstanceID, event.AggregateID) - if err != nil { - return err - } - return v.ProcessedStylingSequence(event) -} - -func (v *View) GetLatestStylingSequence(ctx context.Context, instanceID string) (*global_view.CurrentSequence, error) { - return v.latestSequence(ctx, stylingTyble, instanceID) -} - -func (v *View) GetLatestStylingSequences(ctx context.Context, instanceIDs []string) ([]*global_view.CurrentSequence, error) { - return v.latestSequences(ctx, stylingTyble, instanceIDs) -} - -func (v *View) ProcessedStylingSequence(event *models.Event) error { - return v.saveCurrentSequence(stylingTyble, event) -} - -func (v *View) UpdateStylingSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(stylingTyble, instanceIDs) -} - -func (v *View) GetLatestStylingFailedEvent(sequence uint64, instanceID string) (*global_view.FailedEvent, error) { - return v.latestFailedEvent(stylingTyble, instanceID, sequence) -} - -func (v *View) ProcessedStylingFailedEvent(failedEvent *global_view.FailedEvent) error { - return v.saveFailedEvent(failedEvent) +func (v *View) UpdateOrgOwnerRemovedStyling(event eventstore.Event) error { + return view.UpdateOrgOwnerRemovedStyling(v.Db, stylingTyble, event.Aggregate().InstanceID, event.Aggregate().ID) } diff --git a/internal/admin/repository/repository.go b/internal/admin/repository/repository.go deleted file mode 100644 index cefaf02a3..000000000 --- a/internal/admin/repository/repository.go +++ /dev/null @@ -1,8 +0,0 @@ -package repository - -import "context" - -type Repository interface { - Health(ctx context.Context) error - AdministratorRepository -} diff --git a/internal/api/authz/context.go b/internal/api/authz/context.go index 0d022879c..a916dfcc8 100644 --- a/internal/api/authz/context.go +++ b/internal/api/authz/context.go @@ -91,13 +91,7 @@ func VerifyTokenAndCreateCtxData(ctx context.Context, token, orgID, orgDomain st verifiedOrgID, err := t.ExistsOrg(ctx, orgID, orgDomain) if err != nil { - err = retry(func() error { - verifiedOrgID, err = t.ExistsOrg(ctx, orgID, orgDomain) - return err - }) - if err != nil { - return CtxData{}, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist") - } + return CtxData{}, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist") } return CtxData{ diff --git a/internal/api/authz/permissions.go b/internal/api/authz/permissions.go index 4312651bd..a7f26b2d0 100644 --- a/internal/api/authz/permissions.go +++ b/internal/api/authz/permissions.go @@ -16,10 +16,8 @@ func CheckPermission(ctx context.Context, resolver MembershipsResolver, roleMapp _, userPermissionSpan := tracing.NewNamedSpan(ctx, "checkUserPermissions") err = checkUserResourcePermissions(requestedPermissions, resourceID) userPermissionSpan.EndWithError(err) - if err != nil { - return err - } - return nil + + return err } // getUserPermissions retrieves the memberships of the authenticated user (on instance and provided organisation level), @@ -33,23 +31,17 @@ func getUserPermissions(ctx context.Context, resolver MembershipsResolver, requi } ctx = context.WithValue(ctx, dataKey, ctxData) - memberships, err := resolver.SearchMyMemberships(ctx, orgID) + memberships, err := resolver.SearchMyMemberships(ctx, orgID, false) if err != nil { return nil, nil, err } if len(memberships) == 0 { - err = retry(func() error { - memberships, err = resolver.SearchMyMemberships(ctx, orgID) - if err != nil { - return err - } - if len(memberships) == 0 { - return errors.ThrowNotFound(nil, "AUTHZ-cdgFk", "membership not found") - } - return nil - }) + memberships, err = resolver.SearchMyMemberships(ctx, orgID, true) + if len(memberships) == 0 { + return nil, nil, errors.ThrowNotFound(nil, "AUTHZ-cdgFk", "membership not found") + } if err != nil { - return nil, nil, nil + return nil, nil, err } } requestedPermissions, allPermissions = mapMembershipsToPermissions(requiredPerm, memberships, roleMappings) diff --git a/internal/api/authz/permissions_test.go b/internal/api/authz/permissions_test.go index 01cfa3543..41b1ff2b8 100644 --- a/internal/api/authz/permissions_test.go +++ b/internal/api/authz/permissions_test.go @@ -18,7 +18,7 @@ type testVerifier struct { func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID, projectID string) (string, string, string, string, string, error) { return "userID", "agentID", "clientID", "de", "orgID", nil } -func (v *testVerifier) SearchMyMemberships(ctx context.Context, orgID string) ([]*Membership, error) { +func (v *testVerifier) SearchMyMemberships(ctx context.Context, orgID string, _ bool) ([]*Membership, error) { return v.memberships, nil } diff --git a/internal/api/authz/retry.go b/internal/api/authz/retry.go deleted file mode 100644 index b22cb542f..000000000 --- a/internal/api/authz/retry.go +++ /dev/null @@ -1,15 +0,0 @@ -package authz - -import "time" - -//TODO: workaround if org projection is not yet up-to-date -func retry(retriable func() error) (err error) { - for i := 0; i < 3; i++ { - time.Sleep(500 * time.Millisecond) - err = retriable() - if err == nil { - return nil - } - } - return err -} diff --git a/internal/api/authz/retry_test.go b/internal/api/authz/retry_test.go deleted file mode 100644 index 685509b95..000000000 --- a/internal/api/authz/retry_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package authz - -import ( - "errors" - "testing" -) - -func Test_retry(t *testing.T) { - type args struct { - retriable func(*int) func() error - } - type want struct { - executions int - err bool - } - tests := []struct { - name string - args args - want want - }{ - { - name: "1 execution", - args: args{ - retriable: func(execs *int) func() error { - return func() error { - if *execs < 1 { - *execs++ - return errors.New("not 1") - } - return nil - } - }, - }, - want: want{ - err: false, - executions: 1, - }, - }, - { - name: "2 execution", - args: args{ - retriable: func(execs *int) func() error { - return func() error { - if *execs < 2 { - *execs++ - return errors.New("not 2") - } - return nil - } - }, - }, - want: want{ - err: false, - executions: 2, - }, - }, - { - name: "too many execution", - args: args{ - retriable: func(execs *int) func() error { - return func() error { - if *execs < 3 { - *execs++ - return errors.New("not 3") - } - return nil - } - }, - }, - want: want{ - err: true, - executions: 3, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var execs int - - if err := retry(tt.args.retriable(&execs)); (err != nil) != tt.want.err { - t.Errorf("retry() error = %v, want.err %v", err, tt.want.err) - } - if execs != tt.want.executions { - t.Errorf("retry() executions: want: %d got: %d", tt.want.executions, execs) - } - }) - } -} diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index 615543093..afb4af4bf 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -32,13 +32,13 @@ type TokenVerifier struct { } type MembershipsResolver interface { - SearchMyMemberships(ctx context.Context, orgID string) ([]*Membership, error) + SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) ([]*Membership, error) } type authZRepo interface { + MembershipsResolver VerifyAccessToken(ctx context.Context, token, verifierClientID, projectID string) (userID, agentID, clientID, prefLang, resourceOwner string, err error) VerifierClientID(ctx context.Context, name string) (clientID, projectID string, err error) - SearchMyMemberships(ctx context.Context, orgID string) ([]*Membership, error) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error) ExistsOrg(ctx context.Context, id, domain string) (string, error) } @@ -135,10 +135,10 @@ func (v *TokenVerifier) RegisterServer(appName, methodPrefix string, mappings Me } } -func (v *TokenVerifier) SearchMyMemberships(ctx context.Context, orgID string) (_ []*Membership, err error) { +func (v *TokenVerifier) SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) (_ []*Membership, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - return v.authZRepo.SearchMyMemberships(ctx, orgID) + return v.authZRepo.SearchMyMemberships(ctx, orgID, shouldTriggerBulk) } func (v *TokenVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (_ string, _ []string, err error) { diff --git a/internal/api/grpc/admin/event.go b/internal/api/grpc/admin/event.go index 04f7026fb..2c7a2fe63 100644 --- a/internal/api/grpc/admin/event.go +++ b/internal/api/grpc/admin/event.go @@ -57,15 +57,19 @@ func eventRequestToFilter(ctx context.Context, req *admin_pb.ListEventsRequest) OrderDesc(). InstanceID(authz.GetInstance(ctx).InstanceID()). Limit(limit). + AwaitOpenTransactions(). ResourceOwner(req.ResourceOwner). EditorUser(req.EditorUserId). - AddQuery(). - AggregateIDs(aggregateIDs...). - AggregateTypes(aggregateTypes...). - EventTypes(eventTypes...). CreationDateAfter(req.CreationDate.AsTime()). - SequenceGreater(req.Sequence). - Builder() + SequenceGreater(req.Sequence) + + if len(aggregateIDs) > 0 || len(aggregateTypes) > 0 || len(eventTypes) > 0 { + builder.AddQuery(). + AggregateIDs(aggregateIDs...). + AggregateTypes(aggregateTypes...). + EventTypes(eventTypes...). + Builder() + } if req.Asc { builder.OrderAsc() diff --git a/internal/api/grpc/admin/failed_event.go b/internal/api/grpc/admin/failed_event.go index c6bbf98d1..fd08de377 100644 --- a/internal/api/grpc/admin/failed_event.go +++ b/internal/api/grpc/admin/failed_event.go @@ -10,11 +10,6 @@ import ( func (s *Server) ListFailedEvents(ctx context.Context, _ *admin_pb.ListFailedEventsRequest) (*admin_pb.ListFailedEventsResponse, error) { instanceID := authz.GetInstance(ctx).InstanceID() - failedEventsOld, err := s.administrator.GetFailedEvents(ctx, instanceID) - if err != nil { - return nil, err - } - convertedOld := FailedEventsViewToPb(failedEventsOld) instanceIDQuery, err := query.NewFailedEventInstanceIDSearchQuery(instanceID) if err != nil { return nil, err @@ -25,17 +20,11 @@ func (s *Server) ListFailedEvents(ctx context.Context, _ *admin_pb.ListFailedEve if err != nil { return nil, err } - convertedNew := FailedEventsToPb(s.database, failedEvents) - return &admin_pb.ListFailedEventsResponse{Result: append(convertedOld, convertedNew...)}, nil + return &admin_pb.ListFailedEventsResponse{Result: FailedEventsToPb(s.database, failedEvents)}, nil } func (s *Server) RemoveFailedEvent(ctx context.Context, req *admin_pb.RemoveFailedEventRequest) (*admin_pb.RemoveFailedEventResponse, error) { - var err error - if req.Database != s.database { - err = s.administrator.RemoveFailedEvent(ctx, RemoveFailedEventRequestToModel(ctx, req)) - } else { - err = s.query.RemoveFailedEvent(ctx, req.ViewName, authz.GetInstance(ctx).InstanceID(), req.FailedSequence) - } + err := s.query.RemoveFailedEvent(ctx, req.ViewName, authz.GetInstance(ctx).InstanceID(), req.FailedSequence) if err != nil { return nil, err } diff --git a/internal/api/grpc/admin/failed_event_converter.go b/internal/api/grpc/admin/failed_event_converter.go index 90c040e74..05d7cc6d6 100644 --- a/internal/api/grpc/admin/failed_event_converter.go +++ b/internal/api/grpc/admin/failed_event_converter.go @@ -1,39 +1,12 @@ package admin import ( - "context" - "google.golang.org/protobuf/types/known/timestamppb" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/view/model" admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" ) -func FailedEventsViewToPb(failedEvents []*model.FailedEvent) []*admin_pb.FailedEvent { - events := make([]*admin_pb.FailedEvent, len(failedEvents)) - for i, failedEvent := range failedEvents { - events[i] = FailedEventViewToPb(failedEvent) - } - return events -} - -func FailedEventViewToPb(failedEvent *model.FailedEvent) *admin_pb.FailedEvent { - var lastFailed *timestamppb.Timestamp - if !failedEvent.LastFailed.IsZero() { - lastFailed = timestamppb.New(failedEvent.LastFailed) - } - return &admin_pb.FailedEvent{ - Database: failedEvent.Database, - ViewName: failedEvent.ViewName, - FailedSequence: failedEvent.FailedSequence, - FailureCount: failedEvent.FailureCount, - ErrorMessage: failedEvent.ErrMsg, - LastFailed: lastFailed, - } -} - func FailedEventsToPb(database string, failedEvents *query.FailedEvents) []*admin_pb.FailedEvent { events := make([]*admin_pb.FailedEvent, len(failedEvents.FailedEvents)) for i, failedEvent := range failedEvents.FailedEvents { @@ -56,12 +29,3 @@ func FailedEventToPb(database string, failedEvent *query.FailedEvent) *admin_pb. LastFailed: lastFailed, } } - -func RemoveFailedEventRequestToModel(ctx context.Context, req *admin_pb.RemoveFailedEventRequest) *model.FailedEvent { - return &model.FailedEvent{ - Database: req.Database, - ViewName: req.ViewName, - FailedSequence: req.FailedSequence, - InstanceID: authz.GetInstance(ctx).InstanceID(), - } -} diff --git a/internal/api/grpc/admin/failed_event_converter_test.go b/internal/api/grpc/admin/failed_event_converter_test.go deleted file mode 100644 index 6edc36c1e..000000000 --- a/internal/api/grpc/admin/failed_event_converter_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package admin - -import ( - "context" - "testing" - "time" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/test" - "github.com/zitadel/zitadel/internal/view/model" - admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" -) - -func TestFailedEventsToPbFields(t *testing.T) { - type args struct { - failedEvents []*model.FailedEvent - } - tests := []struct { - name string - args args - }{ - { - name: "all fields", - args: args{ - failedEvents: []*model.FailedEvent{ - { - Database: "admin", - ViewName: "users", - FailedSequence: 456, - FailureCount: 5, - LastFailed: time.Now(), - ErrMsg: "some error", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := FailedEventsViewToPb(tt.args.failedEvents) - for _, g := range got { - test.AssertFieldsMapped(t, g) - } - }) - } -} - -func TestFailedEventToPbFields(t *testing.T) { - type args struct { - failedEvent *model.FailedEvent - } - tests := []struct { - name string - args args - }{ - { - "all fields", - args{ - failedEvent: &model.FailedEvent{ - Database: "admin", - ViewName: "users", - FailedSequence: 456, - FailureCount: 5, - LastFailed: time.Now(), - ErrMsg: "some error", - }, - }, - }, - } - for _, tt := range tests { - converted := FailedEventViewToPb(tt.args.failedEvent) - test.AssertFieldsMapped(t, converted) - } -} - -func TestRemoveFailedEventRequestToModelFields(t *testing.T) { - type args struct { - ctx context.Context - req *admin_pb.RemoveFailedEventRequest - } - tests := []struct { - name string - args args - }{ - { - "all fields", - args{ - ctx: authz.WithInstanceID(context.Background(), "instanceID"), - req: &admin_pb.RemoveFailedEventRequest{ - Database: "admin", - ViewName: "users", - FailedSequence: 456, - }, - }, - }, - } - for _, tt := range tests { - converted := RemoveFailedEventRequestToModel(tt.args.ctx, tt.args.req) - test.AssertFieldsMapped(t, converted, "FailureCount", "LastFailed", "ErrMsg") - } -} diff --git a/internal/api/grpc/admin/iam_member.go b/internal/api/grpc/admin/iam_member.go index 45efdaba7..3e06e8223 100644 --- a/internal/api/grpc/admin/iam_member.go +++ b/internal/api/grpc/admin/iam_member.go @@ -27,7 +27,7 @@ func (s *Server) ListIAMMembers(ctx context.Context, req *admin_pb.ListIAMMember return nil, err } return &admin_pb.ListIAMMembersResponse{ - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), //TODO: resource owner of user of the member instead of the membership resource owner Result: member.MembersToPb("", res.Members), }, nil diff --git a/internal/api/grpc/admin/iam_settings.go b/internal/api/grpc/admin/iam_settings.go index a4be248ee..53a5969d1 100644 --- a/internal/api/grpc/admin/iam_settings.go +++ b/internal/api/grpc/admin/iam_settings.go @@ -19,7 +19,7 @@ func (s *Server) ListSecretGenerators(ctx context.Context, req *admin_pb.ListSec } return &admin_pb.ListSecretGeneratorsResponse{ Result: SecretGeneratorsToPb(result.SecretGenerators), - Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun), }, nil } diff --git a/internal/api/grpc/admin/idp.go b/internal/api/grpc/admin/idp.go index f382946ed..8182ad63b 100644 --- a/internal/api/grpc/admin/idp.go +++ b/internal/api/grpc/admin/idp.go @@ -30,7 +30,7 @@ func (s *Server) ListIDPs(ctx context.Context, req *admin_pb.ListIDPsRequest) (* } return &admin_pb.ListIDPsResponse{ Result: idp_grpc.IDPViewsToPb(resp.IDPs), - Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp), + Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.LastRun), }, nil } @@ -175,7 +175,7 @@ func (s *Server) ListProviders(ctx context.Context, req *admin_pb.ListProvidersR } return &admin_pb.ListProvidersResponse{ Result: idp_grpc.ProvidersToPb(resp.Templates), - Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp), + Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.LastRun), }, nil } diff --git a/internal/api/grpc/admin/instance.go b/internal/api/grpc/admin/instance.go index 4359e73f1..f973c8a32 100644 --- a/internal/api/grpc/admin/instance.go +++ b/internal/api/grpc/admin/instance.go @@ -33,7 +33,7 @@ func (s *Server) ListInstanceDomains(ctx context.Context, req *admin_pb.ListInst Details: object.ToListDetails( domains.Count, domains.Sequence, - domains.Timestamp, + domains.LastRun, ), }, nil } diff --git a/internal/api/grpc/admin/login_policy.go b/internal/api/grpc/admin/login_policy.go index 8d95a6979..6b751bec8 100644 --- a/internal/api/grpc/admin/login_policy.go +++ b/internal/api/grpc/admin/login_policy.go @@ -42,7 +42,7 @@ func (s *Server) ListLoginPolicyIDPs(ctx context.Context, req *admin_pb.ListLogi } return &admin_pb.ListLoginPolicyIDPsResponse{ Result: idp.IDPLoginPolicyLinksToPb(res.Links), - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -88,7 +88,7 @@ func (s *Server) ListLoginPolicySecondFactors(ctx context.Context, req *admin_pb return nil, err } return &admin_pb.ListLoginPolicySecondFactorsResponse{ - Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun), Result: policy_grpc.ModelSecondFactorTypesToPb(result.Factors), }, nil } @@ -119,7 +119,7 @@ func (s *Server) ListLoginPolicyMultiFactors(ctx context.Context, req *admin_pb. return nil, err } return &admin_pb.ListLoginPolicyMultiFactorsResponse{ - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), Result: policy_grpc.ModelMultiFactorTypesToPb(res.Factors), }, nil } diff --git a/internal/api/grpc/admin/org.go b/internal/api/grpc/admin/org.go index 80cb76571..7b95494d0 100644 --- a/internal/api/grpc/admin/org.go +++ b/internal/api/grpc/admin/org.go @@ -61,7 +61,7 @@ func (s *Server) ListOrgs(ctx context.Context, req *admin_pb.ListOrgsRequest) (* } return &admin_pb.ListOrgsResponse{ Result: org_grpc.OrgViewsToPb(orgs.Orgs), - Details: object.ToListDetails(orgs.Count, orgs.Sequence, orgs.Timestamp), + Details: object.ToListDetails(orgs.Count, orgs.Sequence, orgs.LastRun), }, nil } diff --git a/internal/api/grpc/admin/server.go b/internal/api/grpc/admin/server.go index d25bd4b3d..7fe8e32f3 100644 --- a/internal/api/grpc/admin/server.go +++ b/internal/api/grpc/admin/server.go @@ -6,7 +6,6 @@ import ( "google.golang.org/grpc" - "github.com/zitadel/zitadel/internal/admin/repository" "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing" "github.com/zitadel/zitadel/internal/api/assets" "github.com/zitadel/zitadel/internal/api/authz" @@ -29,7 +28,6 @@ type Server struct { database string command *command.Commands query *query.Queries - administrator repository.AdministratorRepository assetsAPIDomain func(context.Context) string userCodeAlg crypto.EncryptionAlgorithm passwordHashAlg crypto.HashAlgorithm @@ -45,7 +43,6 @@ func CreateServer( command *command.Commands, query *query.Queries, sd systemdefaults.SystemDefaults, - repo repository.Repository, externalSecure bool, userCodeAlg crypto.EncryptionAlgorithm, auditLogRetention time.Duration, @@ -54,7 +51,6 @@ func CreateServer( database: database, command: command, query: query, - administrator: repo, assetsAPIDomain: assets.AssetAPI(externalSecure), userCodeAlg: userCodeAlg, passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost), diff --git a/internal/api/grpc/admin/sms.go b/internal/api/grpc/admin/sms.go index 9f46eb8b6..18e11b23d 100644 --- a/internal/api/grpc/admin/sms.go +++ b/internal/api/grpc/admin/sms.go @@ -18,7 +18,7 @@ func (s *Server) ListSMSProviders(ctx context.Context, req *admin_pb.ListSMSProv return nil, err } return &admin_pb.ListSMSProvidersResponse{ - Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun), Result: SMSConfigsToPb(result.Configs), }, nil } diff --git a/internal/api/grpc/admin/view.go b/internal/api/grpc/admin/view.go index d7fa30f42..ef281ae2c 100644 --- a/internal/api/grpc/admin/view.go +++ b/internal/api/grpc/admin/view.go @@ -10,23 +10,15 @@ import ( func (s *Server) ListViews(ctx context.Context, _ *admin_pb.ListViewsRequest) (*admin_pb.ListViewsResponse, error) { instanceID := authz.GetInstance(ctx).InstanceID() - instanceIDQuery, err := query.NewCurrentSequencesInstanceIDSearchQuery(instanceID) + instanceIDQuery, err := query.NewCurrentStatesInstanceIDSearchQuery(instanceID) if err != nil { return nil, err } - currentSequences, err := s.query.SearchCurrentSequences(ctx, &query.CurrentSequencesSearchQueries{ + currentSequences, err := s.query.SearchCurrentStates(ctx, &query.CurrentStateSearchQueries{ Queries: []query.SearchQuery{instanceIDQuery}, }) if err != nil { return nil, err } - convertedCurrentSequences := CurrentSequencesToPb(s.database, currentSequences) - views, err := s.administrator.GetViews(instanceID) - if err != nil { - return nil, err - } - convertedViews := ViewsToPb(views) - - convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...) - return &admin_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil + return &admin_pb.ListViewsResponse{Result: CurrentSequencesToPb(s.database, currentSequences)}, nil } diff --git a/internal/api/grpc/admin/view_converter.go b/internal/api/grpc/admin/view_converter.go index f9d28185e..9e5b9da94 100644 --- a/internal/api/grpc/admin/view_converter.go +++ b/internal/api/grpc/admin/view_converter.go @@ -4,41 +4,23 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/view/model" admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" ) -func ViewsToPb(views []*model.View) []*admin_pb.View { - v := make([]*admin_pb.View, len(views)) - for i, view := range views { - v[i] = ViewToPb(view) - } - return v -} - -func ViewToPb(view *model.View) *admin_pb.View { - return &admin_pb.View{ - Database: view.Database, - ViewName: view.ViewName, - LastSuccessfulSpoolerRun: timestamppb.New(view.LastSuccessfulSpoolerRun), - ProcessedSequence: view.CurrentSequence, - EventTimestamp: timestamppb.New(view.EventTimestamp), - } -} - -func CurrentSequencesToPb(database string, currentSequences *query.CurrentSequences) []*admin_pb.View { - v := make([]*admin_pb.View, len(currentSequences.CurrentSequences)) - for i, currentSequence := range currentSequences.CurrentSequences { +func CurrentSequencesToPb(database string, currentSequences *query.CurrentStates) []*admin_pb.View { + v := make([]*admin_pb.View, len(currentSequences.CurrentStates)) + for i, currentSequence := range currentSequences.CurrentStates { v[i] = CurrentSequenceToPb(database, currentSequence) } return v } -func CurrentSequenceToPb(database string, currentSequence *query.CurrentSequence) *admin_pb.View { +func CurrentSequenceToPb(database string, currentSequence *query.CurrentState) *admin_pb.View { return &admin_pb.View{ Database: database, ViewName: currentSequence.ProjectionName, - ProcessedSequence: currentSequence.CurrentSequence, - LastSuccessfulSpoolerRun: timestamppb.New(currentSequence.Timestamp), + ProcessedSequence: currentSequence.Sequence, + LastSuccessfulSpoolerRun: timestamppb.New(currentSequence.LastRun), + EventTimestamp: timestamppb.New(currentSequence.EventCreatedAt), } } diff --git a/internal/api/grpc/auth/idp.go b/internal/api/grpc/auth/idp.go index 10879a5d8..9d1759ae8 100644 --- a/internal/api/grpc/auth/idp.go +++ b/internal/api/grpc/auth/idp.go @@ -19,7 +19,7 @@ func (s *Server) ListMyLinkedIDPs(ctx context.Context, req *auth_pb.ListMyLinked } return &auth_pb.ListMyLinkedIDPsResponse{ Result: idp_grpc.IDPUserLinksToPb(links.Links), - Details: object.ToListDetails(links.Count, links.Sequence, links.Timestamp), + Details: object.ToListDetails(links.Count, links.Sequence, links.LastRun), }, nil } diff --git a/internal/api/grpc/auth/permission.go b/internal/api/grpc/auth/permission.go index 5f45542da..4c0019cf5 100644 --- a/internal/api/grpc/auth/permission.go +++ b/internal/api/grpc/auth/permission.go @@ -48,12 +48,12 @@ func (s *Server) ListMyMemberships(ctx context.Context, req *auth_pb.ListMyMembe if err != nil { return nil, err } - response, err := s.query.Memberships(ctx, request, false) + response, err := s.query.Memberships(ctx, request, false, false) if err != nil { return nil, err } return &auth_pb.ListMyMembershipsResponse{ Result: user_grpc.MembershipsToMembershipsPb(response.Memberships), - Details: object.ToListDetails(response.Count, response.Sequence, response.Timestamp), + Details: object.ToListDetails(response.Count, response.Sequence, response.LastRun), }, nil } diff --git a/internal/api/grpc/auth/user.go b/internal/api/grpc/auth/user.go index 41a7fee19..11ecb95cf 100644 --- a/internal/api/grpc/auth/user.go +++ b/internal/api/grpc/auth/user.go @@ -44,7 +44,7 @@ func (s *Server) RemoveMyUser(ctx context.Context, _ *auth_pb.RemoveMyUserReques } memberships, err := s.query.Memberships(ctx, &query.MembershipSearchQuery{ Queries: []query.SearchQuery{userQuery}, - }, false) + }, false, false) if err != nil { return nil, err } @@ -73,9 +73,10 @@ func (s *Server) ListMyUserChanges(ctx context.Context, req *auth_pb.ListMyUserC AllowTimeTravel(). Limit(limit). OrderDesc(). + AwaitOpenTransactions(). ResourceOwner(authz.GetCtxData(ctx).ResourceOwner). - AddQuery(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(user.AggregateType). AggregateIDs(authz.GetCtxData(ctx).UserID). Builder() @@ -104,7 +105,7 @@ func (s *Server) ListMyMetadata(ctx context.Context, req *auth_pb.ListMyMetadata } return &auth_pb.ListMyMetadataResponse{ Result: metadata.UserMetadataListToPb(res.Metadata), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -158,7 +159,7 @@ func (s *Server) ListMyUserGrants(ctx context.Context, req *auth_pb.ListMyUserGr } return &auth_pb.ListMyUserGrantsResponse{ Result: UserGrantsToPb(res.UserGrants), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -225,7 +226,7 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje return nil, err } return &auth_pb.ListMyProjectOrgsResponse{ - Details: obj_grpc.ToListDetails(orgs.Count, orgs.Sequence, orgs.Timestamp), + Details: obj_grpc.ToListDetails(orgs.Count, orgs.Sequence, orgs.LastRun), Result: org.OrgsToPb(orgs.Orgs), }, nil } @@ -237,7 +238,7 @@ func (s *Server) myOrgsQuery(ctx context.Context, ctxData authz.CtxData) (*query } return s.query.Memberships(ctx, &query.MembershipSearchQuery{ Queries: []query.SearchQuery{userQuery}, - }, false) + }, false, false) } func isIAMAdmin(memberships []*query.Membership) bool { diff --git a/internal/api/grpc/management/actions.go b/internal/api/grpc/management/actions.go index 09a013608..eedddd7fc 100644 --- a/internal/api/grpc/management/actions.go +++ b/internal/api/grpc/management/actions.go @@ -19,7 +19,7 @@ func (s *Server) ListActions(ctx context.Context, req *mgmt_pb.ListActionsReques return nil, err } return &mgmt_pb.ListActionsResponse{ - Details: obj_grpc.ToListDetails(actions.Count, actions.Sequence, actions.Timestamp), + Details: obj_grpc.ToListDetails(actions.Count, actions.Sequence, actions.LastRun), Result: action_grpc.ActionsToPb(actions.Actions), }, nil } diff --git a/internal/api/grpc/management/idp.go b/internal/api/grpc/management/idp.go index 11873d848..f01301525 100644 --- a/internal/api/grpc/management/idp.go +++ b/internal/api/grpc/management/idp.go @@ -30,7 +30,7 @@ func (s *Server) ListOrgIDPs(ctx context.Context, req *mgmt_pb.ListOrgIDPsReques } return &mgmt_pb.ListOrgIDPsResponse{ Result: idp_grpc.IDPViewsToPb(resp.IDPs), - Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp), + Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.LastRun), }, nil } @@ -167,7 +167,7 @@ func (s *Server) ListProviders(ctx context.Context, req *mgmt_pb.ListProvidersRe } return &mgmt_pb.ListProvidersResponse{ Result: idp_grpc.ProvidersToPb(resp.Templates), - Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp), + Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.LastRun), }, nil } diff --git a/internal/api/grpc/management/org.go b/internal/api/grpc/management/org.go index df8e76c0e..5879cd971 100644 --- a/internal/api/grpc/management/org.go +++ b/internal/api/grpc/management/org.go @@ -52,9 +52,10 @@ func (s *Server) ListOrgChanges(ctx context.Context, req *mgmt_pb.ListOrgChanges AllowTimeTravel(). Limit(limit). OrderDesc(). + AwaitOpenTransactions(). ResourceOwner(authz.GetCtxData(ctx).OrgID). - AddQuery(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(org.AggregateType). AggregateIDs(authz.GetCtxData(ctx).OrgID). Builder() @@ -176,7 +177,7 @@ func (s *Server) ListOrgDomains(ctx context.Context, req *mgmt_pb.ListOrgDomains } return &mgmt_pb.ListOrgDomainsResponse{ Result: org_grpc.DomainsToPb(domains.Domains), - Details: object.ToListDetails(domains.Count, domains.Sequence, domains.Timestamp), + Details: object.ToListDetails(domains.Count, domains.Sequence, domains.LastRun), }, nil } @@ -272,7 +273,7 @@ func (s *Server) ListOrgMembers(ctx context.Context, req *mgmt_pb.ListOrgMembers } return &mgmt_pb.ListOrgMembersResponse{ Result: member_grpc.MembersToPb(s.assetAPIPrefix(ctx), members.Members), - Details: object.ToListDetails(members.Count, members.Sequence, members.Timestamp), + Details: object.ToListDetails(members.Count, members.Sequence, members.LastRun), }, nil } @@ -350,7 +351,7 @@ func (s *Server) ListOrgMetadata(ctx context.Context, req *mgmt_pb.ListOrgMetada } return &mgmt_pb.ListOrgMetadataResponse{ Result: metadata.OrgMetadataListToPb(res.Metadata), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } diff --git a/internal/api/grpc/management/policy_login.go b/internal/api/grpc/management/policy_login.go index d3df45bc8..3aa160353 100644 --- a/internal/api/grpc/management/policy_login.go +++ b/internal/api/grpc/management/policy_login.go @@ -74,7 +74,7 @@ func (s *Server) ListLoginPolicyIDPs(ctx context.Context, req *mgmt_pb.ListLogin } return &mgmt_pb.ListLoginPolicyIDPsResponse{ Result: idp.IDPLoginPolicyLinksToPb(res.Links), - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -123,7 +123,7 @@ func (s *Server) ListLoginPolicySecondFactors(ctx context.Context, req *mgmt_pb. return nil, err } return &mgmt_pb.ListLoginPolicySecondFactorsResponse{ - Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun), Result: policy_grpc.ModelSecondFactorTypesToPb(result.Factors), }, nil } @@ -154,7 +154,7 @@ func (s *Server) ListLoginPolicyMultiFactors(ctx context.Context, req *mgmt_pb.L return nil, err } return &mgmt_pb.ListLoginPolicyMultiFactorsResponse{ - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), Result: policy_grpc.ModelMultiFactorTypesToPb(res.Factors), }, nil } diff --git a/internal/api/grpc/management/project.go b/internal/api/grpc/management/project.go index f0c1f6cc6..9d02dc7a5 100644 --- a/internal/api/grpc/management/project.go +++ b/internal/api/grpc/management/project.go @@ -53,7 +53,7 @@ func (s *Server) ListProjects(ctx context.Context, req *mgmt_pb.ListProjectsRequ } return &mgmt_pb.ListProjectsResponse{ Result: project_grpc.ProjectViewsToPb(projects.Projects), - Details: object_grpc.ToListDetails(projects.Count, projects.Sequence, projects.Timestamp), + Details: object_grpc.ToListDetails(projects.Count, projects.Sequence, projects.LastRun), }, nil } @@ -74,8 +74,9 @@ func (s *Server) ListProjectGrantChanges(ctx context.Context, req *mgmt_pb.ListP Limit(limit). OrderDesc(). ResourceOwner(authz.GetCtxData(ctx).OrgID). - AddQuery(). + AwaitOpenTransactions(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(project.AggregateType). AggregateIDs(req.ProjectId). EventData(map[string]interface{}{ @@ -115,7 +116,7 @@ func (s *Server) ListGrantedProjects(ctx context.Context, req *mgmt_pb.ListGrant } return &mgmt_pb.ListGrantedProjectsResponse{ Result: project_grpc.GrantedProjectViewsToPb(projects.ProjectGrants), - Details: object_grpc.ToListDetails(projects.Count, projects.Sequence, projects.Timestamp), + Details: object_grpc.ToListDetails(projects.Count, projects.Sequence, projects.LastRun), }, nil } @@ -134,7 +135,7 @@ func (s *Server) ListGrantedProjectRoles(ctx context.Context, req *mgmt_pb.ListG } return &mgmt_pb.ListGrantedProjectRolesResponse{ Result: project_grpc.RoleViewsToPb(roles.ProjectRoles), - Details: object_grpc.ToListDetails(roles.Count, roles.Sequence, roles.Timestamp), + Details: object_grpc.ToListDetails(roles.Count, roles.Sequence, roles.LastRun), }, nil } @@ -153,10 +154,11 @@ func (s *Server) ListProjectChanges(ctx context.Context, req *mgmt_pb.ListProjec query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). AllowTimeTravel(). Limit(limit). + AwaitOpenTransactions(). OrderDesc(). ResourceOwner(authz.GetCtxData(ctx).OrgID). - AddQuery(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(project.AggregateType). AggregateIDs(req.ProjectId). Builder() @@ -259,7 +261,7 @@ func (s *Server) ListProjectRoles(ctx context.Context, req *mgmt_pb.ListProjectR } return &mgmt_pb.ListProjectRolesResponse{ Result: project_grpc.RoleViewsToPb(roles.ProjectRoles), - Details: object_grpc.ToListDetails(roles.Count, roles.Sequence, roles.Timestamp), + Details: object_grpc.ToListDetails(roles.Count, roles.Sequence, roles.LastRun), }, nil } @@ -349,7 +351,7 @@ func (s *Server) ListProjectMembers(ctx context.Context, req *mgmt_pb.ListProjec } return &mgmt_pb.ListProjectMembersResponse{ Result: member_grpc.MembersToPb(s.assetAPIPrefix(ctx), members.Members), - Details: object_grpc.ToListDetails(members.Count, members.Sequence, members.Timestamp), + Details: object_grpc.ToListDetails(members.Count, members.Sequence, members.LastRun), }, nil } diff --git a/internal/api/grpc/management/project_application.go b/internal/api/grpc/management/project_application.go index a6b5841c5..ff053089e 100644 --- a/internal/api/grpc/management/project_application.go +++ b/internal/api/grpc/management/project_application.go @@ -36,7 +36,7 @@ func (s *Server) ListApps(ctx context.Context, req *mgmt_pb.ListAppsRequest) (*m } return &mgmt_pb.ListAppsResponse{ Result: project_grpc.AppsToPb(apps.Apps), - Details: object_grpc.ToListDetails(apps.Count, apps.Sequence, apps.Timestamp), + Details: object_grpc.ToListDetails(apps.Count, apps.Sequence, apps.LastRun), }, nil } @@ -55,10 +55,11 @@ func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChanges query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). AllowTimeTravel(). Limit(limit). + AwaitOpenTransactions(). OrderDesc(). ResourceOwner(authz.GetCtxData(ctx).OrgID). - AddQuery(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(project.AggregateType). AggregateIDs(req.ProjectId). EventData(map[string]interface{}{ @@ -278,7 +279,7 @@ func (s *Server) ListAppKeys(ctx context.Context, req *mgmt_pb.ListAppKeysReques } return &mgmt_pb.ListAppKeysResponse{ Result: authn_grpc.KeysToPb(keys.AuthNKeys), - Details: object_grpc.ToListDetails(keys.Count, keys.Sequence, keys.Timestamp), + Details: object_grpc.ToListDetails(keys.Count, keys.Sequence, keys.LastRun), }, nil } diff --git a/internal/api/grpc/management/project_grant.go b/internal/api/grpc/management/project_grant.go index 21db4dc45..d018dad39 100644 --- a/internal/api/grpc/management/project_grant.go +++ b/internal/api/grpc/management/project_grant.go @@ -37,7 +37,7 @@ func (s *Server) ListProjectGrants(ctx context.Context, req *mgmt_pb.ListProject } return &mgmt_pb.ListProjectGrantsResponse{ Result: proj_grpc.GrantedProjectViewsToPb(grants.ProjectGrants), - Details: object_grpc.ToListDetails(grants.Count, grants.Sequence, grants.Timestamp), + Details: object_grpc.ToListDetails(grants.Count, grants.Sequence, grants.LastRun), }, nil } @@ -60,7 +60,7 @@ func (s *Server) ListAllProjectGrants(ctx context.Context, req *mgmt_pb.ListAllP } return &mgmt_pb.ListAllProjectGrantsResponse{ Result: proj_grpc.GrantedProjectViewsToPb(grants.ProjectGrants), - Details: object_grpc.ToListDetails(grants.Count, grants.Sequence, grants.Timestamp), + Details: object_grpc.ToListDetails(grants.Count, grants.Sequence, grants.LastRun), }, nil } @@ -170,7 +170,7 @@ func (s *Server) ListProjectGrantMembers(ctx context.Context, req *mgmt_pb.ListP } return &mgmt_pb.ListProjectGrantMembersResponse{ Result: member_grpc.MembersToPb(s.assetAPIPrefix(ctx), response.Members), - Details: object_grpc.ToListDetails(response.Count, response.Sequence, response.Timestamp), + Details: object_grpc.ToListDetails(response.Count, response.Sequence, response.LastRun), }, nil } diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 804a90521..d7a9c56dc 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -78,7 +78,7 @@ func (s *Server) ListUsers(ctx context.Context, req *mgmt_pb.ListUsersRequest) ( } return &mgmt_pb.ListUsersResponse{ Result: user_grpc.UsersToPb(res.Users, s.assetAPIPrefix(ctx)), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -97,10 +97,11 @@ func (s *Server) ListUserChanges(ctx context.Context, req *mgmt_pb.ListUserChang query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). AllowTimeTravel(). Limit(limit). + AwaitOpenTransactions(). OrderDesc(). ResourceOwner(authz.GetCtxData(ctx).OrgID). - AddQuery(). SequenceGreater(sequence). + AddQuery(). AggregateTypes(user.AggregateType). AggregateIDs(req.UserId). Builder() @@ -151,7 +152,7 @@ func (s *Server) ListUserMetadata(ctx context.Context, req *mgmt_pb.ListUserMeta } return &mgmt_pb.ListUserMetadataResponse{ Result: metadata.UserMetadataListToPb(res.Metadata), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } @@ -383,7 +384,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]* } memberships, err := s.query.Memberships(ctx, &query.MembershipSearchQuery{ Queries: []query.SearchQuery{membershipsUserQuery}, - }, true) + }, true, false) if err != nil { return nil, nil, err } @@ -752,7 +753,7 @@ func (s *Server) ListMachineKeys(ctx context.Context, req *mgmt_pb.ListMachineKe } return &mgmt_pb.ListMachineKeysResponse{ Result: authn.KeysToPb(result.AuthNKeys), - Details: obj_grpc.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: obj_grpc.ToListDetails(result.Count, result.Sequence, result.LastRun), }, nil } @@ -845,7 +846,7 @@ func (s *Server) ListPersonalAccessTokens(ctx context.Context, req *mgmt_pb.List } return &mgmt_pb.ListPersonalAccessTokensResponse{ Result: user_grpc.PersonalAccessTokensToPb(result.PersonalAccessTokens), - Details: obj_grpc.ToListDetails(result.Count, result.Sequence, result.Timestamp), + Details: obj_grpc.ToListDetails(result.Count, result.Sequence, result.LastRun), }, nil } @@ -884,7 +885,7 @@ func (s *Server) ListHumanLinkedIDPs(ctx context.Context, req *mgmt_pb.ListHuman } return &mgmt_pb.ListHumanLinkedIDPsResponse{ Result: idp_grpc.IDPUserLinksToPb(res.Links), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } func (s *Server) RemoveHumanLinkedIDP(ctx context.Context, req *mgmt_pb.RemoveHumanLinkedIDPRequest) (*mgmt_pb.RemoveHumanLinkedIDPResponse, error) { @@ -902,13 +903,13 @@ func (s *Server) ListUserMemberships(ctx context.Context, req *mgmt_pb.ListUserM if err != nil { return nil, err } - response, err := s.query.Memberships(ctx, request, false) + response, err := s.query.Memberships(ctx, request, false, false) if err != nil { return nil, err } return &mgmt_pb.ListUserMembershipsResponse{ Result: user_grpc.MembershipsToMembershipsPb(response.Memberships), - Details: obj_grpc.ToListDetails(response.Count, response.Sequence, response.Timestamp), + Details: obj_grpc.ToListDetails(response.Count, response.Sequence, response.LastRun), }, nil } diff --git a/internal/api/grpc/management/user_grant.go b/internal/api/grpc/management/user_grant.go index 7dc4a1e58..b13a01976 100644 --- a/internal/api/grpc/management/user_grant.go +++ b/internal/api/grpc/management/user_grant.go @@ -39,7 +39,7 @@ func (s *Server) ListUserGrants(ctx context.Context, req *mgmt_pb.ListUserGrantR } return &mgmt_pb.ListUserGrantResponse{ Result: user.UserGrantsToPb(s.assetAPIPrefix(ctx), res.UserGrants), - Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: obj_grpc.ToListDetails(res.Count, res.Sequence, res.LastRun), }, nil } diff --git a/internal/api/grpc/management/user_integration_test.go b/internal/api/grpc/management/user_integration_test.go index ab1e149bd..6b5fe77d1 100644 --- a/internal/api/grpc/management/user_integration_test.go +++ b/internal/api/grpc/management/user_integration_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/text/language" "google.golang.org/grpc/codes" @@ -49,7 +48,6 @@ func TestMain(m *testing.M) { // Get calls would return a Not Found error. func TestImport_and_Get(t *testing.T) { const N = 100 - var misses int for i := 0; i < N; i++ { firstName := strconv.Itoa(i) @@ -76,18 +74,11 @@ func TestImport_and_Get(t *testing.T) { _, err = Client.GetUserByID(CTX, &management.GetUserByIDRequest{Id: res.GetUserId()}) - if s, ok := status.FromError(err); ok { - if s == nil { - return - } - if s.Code() == codes.NotFound { - t.Log(s) - misses++ - return - } + s, ok := status.FromError(err) + if ok && s != nil && s.Code() == codes.NotFound { + t.Errorf("iteration %d: user with id %q not found", i, res.GetUserId()) } require.NoError(t, err) // catch and fail on any other error }) } - assert.Zerof(t, misses, "Not Found errors %d out of %d", misses, N) } diff --git a/internal/api/grpc/object/converter.go b/internal/api/grpc/object/converter.go index 1fd703c77..85fdf5fba 100644 --- a/internal/api/grpc/object/converter.go +++ b/internal/api/grpc/object/converter.go @@ -82,7 +82,7 @@ func AddToDetailsPb( } func ToListDetails( - totalResult, + totalResult uint64, processedSequence uint64, viewTimestamp time.Time, ) *object_pb.ListDetails { diff --git a/internal/api/grpc/object/v2/converter.go b/internal/api/grpc/object/v2/converter.go index 36c6a322b..7253c919b 100644 --- a/internal/api/grpc/object/v2/converter.go +++ b/internal/api/grpc/object/v2/converter.go @@ -26,9 +26,7 @@ func ToListDetails(response query.SearchResponse) *object.ListDetails { details := &object.ListDetails{ TotalResult: response.Count, ProcessedSequence: response.Sequence, - } - if !response.Timestamp.IsZero() { - details.Timestamp = timestamppb.New(response.Timestamp) + Timestamp: timestamppb.New(response.EventCreatedAt), } return details diff --git a/internal/api/grpc/server/middleware/auth_interceptor_test.go b/internal/api/grpc/server/middleware/auth_interceptor_test.go index ac091e1d1..6e22d1352 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor_test.go +++ b/internal/api/grpc/server/middleware/auth_interceptor_test.go @@ -24,7 +24,7 @@ type verifierMock struct{} func (v *verifierMock) VerifyAccessToken(ctx context.Context, token, clientID, projectID string) (string, string, string, string, string, error) { return "", "", "", "", "", nil } -func (v *verifierMock) SearchMyMemberships(ctx context.Context, orgID string) ([]*authz.Membership, error) { +func (v *verifierMock) SearchMyMemberships(ctx context.Context, orgID string, _ bool) ([]*authz.Membership, error) { return nil, nil } diff --git a/internal/api/grpc/system/failed_event.go b/internal/api/grpc/system/failed_event.go index c01afea60..6371ddc3b 100644 --- a/internal/api/grpc/system/failed_event.go +++ b/internal/api/grpc/system/failed_event.go @@ -8,27 +8,15 @@ import ( ) func (s *Server) ListFailedEvents(ctx context.Context, _ *system_pb.ListFailedEventsRequest) (*system_pb.ListFailedEventsResponse, error) { - failedEventsOld, err := s.administrator.GetFailedEvents(ctx, "") - if err != nil { - return nil, err - } - convertedOld := FailedEventsViewToPb(failedEventsOld) - failedEvents, err := s.query.SearchFailedEvents(ctx, new(query.FailedEventSearchQueries)) if err != nil { return nil, err } - convertedNew := FailedEventsToPb(s.database, failedEvents) - return &system_pb.ListFailedEventsResponse{Result: append(convertedOld, convertedNew...)}, nil + return &system_pb.ListFailedEventsResponse{Result: FailedEventsToPb(s.database, failedEvents)}, nil } func (s *Server) RemoveFailedEvent(ctx context.Context, req *system_pb.RemoveFailedEventRequest) (*system_pb.RemoveFailedEventResponse, error) { - var err error - if req.Database != s.database { - err = s.administrator.RemoveFailedEvent(ctx, RemoveFailedEventRequestToModel(req)) - } else { - err = s.query.RemoveFailedEvent(ctx, req.ViewName, req.InstanceId, req.FailedSequence) - } + err := s.query.RemoveFailedEvent(ctx, req.ViewName, req.InstanceId, req.FailedSequence) if err != nil { return nil, err } diff --git a/internal/api/grpc/system/failed_event_converter.go b/internal/api/grpc/system/failed_event_converter.go index 65316477a..96dd71889 100644 --- a/internal/api/grpc/system/failed_event_converter.go +++ b/internal/api/grpc/system/failed_event_converter.go @@ -4,33 +4,9 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/view/model" system_pb "github.com/zitadel/zitadel/pkg/grpc/system" ) -func FailedEventsViewToPb(failedEvents []*model.FailedEvent) []*system_pb.FailedEvent { - events := make([]*system_pb.FailedEvent, len(failedEvents)) - for i, failedEvent := range failedEvents { - events[i] = FailedEventViewToPb(failedEvent) - } - return events -} - -func FailedEventViewToPb(failedEvent *model.FailedEvent) *system_pb.FailedEvent { - var lastFailed *timestamppb.Timestamp - if !failedEvent.LastFailed.IsZero() { - lastFailed = timestamppb.New(failedEvent.LastFailed) - } - return &system_pb.FailedEvent{ - Database: failedEvent.Database, - ViewName: failedEvent.ViewName, - FailedSequence: failedEvent.FailedSequence, - FailureCount: failedEvent.FailureCount, - ErrorMessage: failedEvent.ErrMsg, - LastFailed: lastFailed, - } -} - func FailedEventsToPb(database string, failedEvents *query.FailedEvents) []*system_pb.FailedEvent { events := make([]*system_pb.FailedEvent, len(failedEvents.FailedEvents)) for i, failedEvent := range failedEvents.FailedEvents { @@ -53,12 +29,3 @@ func FailedEventToPb(database string, failedEvent *query.FailedEvent) *system_pb LastFailed: lastFailed, } } - -func RemoveFailedEventRequestToModel(req *system_pb.RemoveFailedEventRequest) *model.FailedEvent { - return &model.FailedEvent{ - Database: req.Database, - ViewName: req.ViewName, - FailedSequence: req.FailedSequence, - InstanceID: req.InstanceId, - } -} diff --git a/internal/api/grpc/system/failed_event_converter_test.go b/internal/api/grpc/system/failed_event_converter_test.go deleted file mode 100644 index f91c2d1b4..000000000 --- a/internal/api/grpc/system/failed_event_converter_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package system_test - -import ( - "testing" - "time" - - system_grpc "github.com/zitadel/zitadel/internal/api/grpc/system" - "github.com/zitadel/zitadel/internal/test" - "github.com/zitadel/zitadel/internal/view/model" - system_pb "github.com/zitadel/zitadel/pkg/grpc/system" -) - -func TestFailedEventsToPbFields(t *testing.T) { - type args struct { - failedEvents []*model.FailedEvent - } - tests := []struct { - name string - args args - }{ - { - name: "all fields", - args: args{ - failedEvents: []*model.FailedEvent{ - { - Database: "admin", - ViewName: "users", - FailedSequence: 456, - FailureCount: 5, - LastFailed: time.Now(), - ErrMsg: "some error", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := system_grpc.FailedEventsViewToPb(tt.args.failedEvents) - for _, g := range got { - test.AssertFieldsMapped(t, g) - } - }) - } -} - -func TestFailedEventToPbFields(t *testing.T) { - type args struct { - failedEvent *model.FailedEvent - } - tests := []struct { - name string - args args - }{ - { - "all fields", - args{ - failedEvent: &model.FailedEvent{ - Database: "admin", - ViewName: "users", - FailedSequence: 456, - FailureCount: 5, - LastFailed: time.Now(), - ErrMsg: "some error", - }, - }, - }, - } - for _, tt := range tests { - converted := system_grpc.FailedEventViewToPb(tt.args.failedEvent) - test.AssertFieldsMapped(t, converted) - } -} - -func TestRemoveFailedEventRequestToModelFields(t *testing.T) { - type args struct { - req *system_pb.RemoveFailedEventRequest - } - tests := []struct { - name string - args args - }{ - { - "all fields", - args{ - req: &system_pb.RemoveFailedEventRequest{ - Database: "admin", - ViewName: "users", - FailedSequence: 456, - InstanceId: "instanceID", - }, - }, - }, - } - for _, tt := range tests { - converted := system_grpc.RemoveFailedEventRequestToModel(tt.args.req) - test.AssertFieldsMapped(t, converted, "FailureCount", "LastFailed", "ErrMsg") - } -} diff --git a/internal/api/grpc/system/instance.go b/internal/api/grpc/system/instance.go index 1d711062c..b4a1386fb 100644 --- a/internal/api/grpc/system/instance.go +++ b/internal/api/grpc/system/instance.go @@ -103,7 +103,7 @@ func (s *Server) ListIAMMembers(ctx context.Context, req *system_pb.ListIAMMembe return nil, err } return &system_pb.ListIAMMembersResponse{ - Details: object.ToListDetails(res.Count, res.Sequence, res.Timestamp), + Details: object.ToListDetails(res.Count, res.Sequence, res.LastRun), //TODO: resource owner of user of the member instead of the membership resource owner Result: member.MembersToPb("", res.Members), }, nil @@ -146,7 +146,7 @@ func (s *Server) ListDomains(ctx context.Context, req *system_pb.ListDomainsRequ } return &system_pb.ListDomainsResponse{ Result: instance_grpc.DomainsToPb(domains.Domains), - Details: object.ToListDetails(domains.Count, domains.Sequence, domains.Timestamp), + Details: object.ToListDetails(domains.Count, domains.Sequence, domains.LastRun), }, nil } diff --git a/internal/api/grpc/system/server.go b/internal/api/grpc/system/server.go index dd947c950..3995ca28f 100644 --- a/internal/api/grpc/system/server.go +++ b/internal/api/grpc/system/server.go @@ -3,7 +3,6 @@ package system import ( "google.golang.org/grpc" - "github.com/zitadel/zitadel/internal/admin/repository" "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/server" @@ -23,7 +22,6 @@ type Server struct { database string command *command.Commands query *query.Queries - administrator repository.AdministratorRepository defaultInstance command.InstanceSetup externalDomain string } @@ -35,7 +33,6 @@ type Config struct { func CreateServer( command *command.Commands, query *query.Queries, - repo repository.Repository, database string, defaultInstance command.InstanceSetup, externalDomain string, @@ -43,7 +40,6 @@ func CreateServer( return &Server{ command: command, query: query, - administrator: repo, database: database, defaultInstance: defaultInstance, externalDomain: externalDomain, diff --git a/internal/api/grpc/system/view.go b/internal/api/grpc/system/view.go index 261b1d40b..f89b925d4 100644 --- a/internal/api/grpc/system/view.go +++ b/internal/api/grpc/system/view.go @@ -8,28 +8,15 @@ import ( ) func (s *Server) ListViews(ctx context.Context, _ *system_pb.ListViewsRequest) (*system_pb.ListViewsResponse, error) { - currentSequences, err := s.query.SearchCurrentSequences(ctx, new(query.CurrentSequencesSearchQueries)) + currentSequences, err := s.query.SearchCurrentStates(ctx, new(query.CurrentStateSearchQueries)) if err != nil { return nil, err } - convertedCurrentSequences := CurrentSequencesToPb(s.database, currentSequences) - views, err := s.administrator.GetViews("") - if err != nil { - return nil, err - } - convertedViews := ViewsToPb(views) - - convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...) - return &system_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil + return &system_pb.ListViewsResponse{Result: CurrentSequencesToPb(s.database, currentSequences)}, nil } func (s *Server) ClearView(ctx context.Context, req *system_pb.ClearViewRequest) (*system_pb.ClearViewResponse, error) { - var err error - if req.Database != s.database { - err = s.administrator.ClearView(ctx, req.Database, req.ViewName) - } else { - err = s.query.ClearCurrentSequence(ctx, req.ViewName) - } + err := s.query.ClearCurrentSequence(ctx, req.ViewName) if err != nil { return nil, err } diff --git a/internal/api/grpc/system/view_converter.go b/internal/api/grpc/system/view_converter.go index 6b11376c5..60d4c623e 100644 --- a/internal/api/grpc/system/view_converter.go +++ b/internal/api/grpc/system/view_converter.go @@ -4,41 +4,22 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/view/model" system_pb "github.com/zitadel/zitadel/pkg/grpc/system" ) -func ViewsToPb(views []*model.View) []*system_pb.View { - v := make([]*system_pb.View, len(views)) - for i, view := range views { - v[i] = ViewToPb(view) - } - return v -} - -func ViewToPb(view *model.View) *system_pb.View { - return &system_pb.View{ - Database: view.Database, - ViewName: view.ViewName, - LastSuccessfulSpoolerRun: timestamppb.New(view.LastSuccessfulSpoolerRun), - ProcessedSequence: view.CurrentSequence, - EventTimestamp: timestamppb.New(view.EventTimestamp), - } -} - -func CurrentSequencesToPb(database string, currentSequences *query.CurrentSequences) []*system_pb.View { - v := make([]*system_pb.View, len(currentSequences.CurrentSequences)) - for i, currentSequence := range currentSequences.CurrentSequences { +func CurrentSequencesToPb(database string, currentSequences *query.CurrentStates) []*system_pb.View { + v := make([]*system_pb.View, len(currentSequences.CurrentStates)) + for i, currentSequence := range currentSequences.CurrentStates { v[i] = CurrentSequenceToPb(database, currentSequence) } return v } -func CurrentSequenceToPb(database string, currentSequence *query.CurrentSequence) *system_pb.View { +func CurrentSequenceToPb(database string, currentSequence *query.CurrentState) *system_pb.View { return &system_pb.View{ Database: database, ViewName: currentSequence.ProjectionName, - ProcessedSequence: currentSequence.CurrentSequence, - LastSuccessfulSpoolerRun: timestamppb.New(currentSequence.Timestamp), + ProcessedSequence: currentSequence.Sequence, + LastSuccessfulSpoolerRun: timestamppb.New(currentSequence.LastRun), } } diff --git a/internal/api/grpc/user/v2/password_integration_test.go b/internal/api/grpc/user/v2/password_integration_test.go index b0074982b..517bde0a4 100644 --- a/internal/api/grpc/user/v2/password_integration_test.go +++ b/internal/api/grpc/user/v2/password_integration_test.go @@ -158,10 +158,7 @@ func TestServer_SetPassword(t *testing.T) { Password: "InitialPassw0rd!", }, }) - if err != nil { - return err - } - return nil + return err }, args: args{ ctx: CTX, diff --git a/internal/api/grpc/user/v2/user_integration_test.go b/internal/api/grpc/user/v2/user_integration_test.go index 4bb1fe871..55f0e76a1 100644 --- a/internal/api/grpc/user/v2/user_integration_test.go +++ b/internal/api/grpc/user/v2/user_integration_test.go @@ -1136,7 +1136,7 @@ func TestServer_ListAuthenticationMethodTypes(t *testing.T) { for { got, err = Client.ListAuthenticationMethodTypes(tt.args.ctx, tt.args.req) - if err == nil && got.GetDetails().GetProcessedSequence() >= idpLink.GetDetails().GetSequence() { + if err == nil && !got.GetDetails().GetTimestamp().AsTime().Before(idpLink.GetDetails().GetChangeDate().AsTime()) { break } select { diff --git a/internal/api/oidc/key.go b/internal/api/oidc/key.go index 237af7db4..82884101d 100644 --- a/internal/api/oidc/key.go +++ b/internal/api/oidc/key.go @@ -121,15 +121,15 @@ func (o *OPStorage) getSigningKey(ctx context.Context) (op.SigningKey, error) { if len(keys.Keys) > 0 { return o.privateKeyToSigningKey(selectSigningKey(keys.Keys)) } - var sequence uint64 - if keys.LatestSequence != nil { - sequence = keys.LatestSequence.Sequence + var position float64 + if keys.State != nil { + position = keys.State.Position } - return nil, o.refreshSigningKey(ctx, o.signingKeyAlgorithm, sequence) + return nil, o.refreshSigningKey(ctx, o.signingKeyAlgorithm, position) } -func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, sequence uint64) error { - ok, err := o.ensureIsLatestKey(ctx, sequence) +func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, position float64) error { + ok, err := o.ensureIsLatestKey(ctx, position) if err != nil || !ok { return errors.ThrowInternal(err, "OIDC-ASfh3", "cannot ensure that projection is up to date") } @@ -140,12 +140,12 @@ func (o *OPStorage) refreshSigningKey(ctx context.Context, algorithm string, seq return errors.ThrowInternal(nil, "OIDC-Df1bh", "") } -func (o *OPStorage) ensureIsLatestKey(ctx context.Context, sequence uint64) (bool, error) { +func (o *OPStorage) ensureIsLatestKey(ctx context.Context, position float64) (bool, error) { maxSequence, err := o.getMaxKeySequence(ctx) if err != nil { return false, fmt.Errorf("error retrieving new events: %w", err) } - return sequence == maxSequence, nil + return position >= maxSequence, nil } func (o *OPStorage) privateKeyToSigningKey(key query.PrivateKey) (_ op.SigningKey, err error) { @@ -183,13 +183,20 @@ func (o *OPStorage) lockAndGenerateSigningKeyPair(ctx context.Context, algorithm return o.command.GenerateSigningKeyPair(setOIDCCtx(ctx), algorithm) } -func (o *OPStorage) getMaxKeySequence(ctx context.Context) (uint64, error) { +func (o *OPStorage) getMaxKeySequence(ctx context.Context) (float64, error) { return o.eventstore.LatestSequence(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence). ResourceOwner(authz.GetInstance(ctx).InstanceID()). + AwaitOpenTransactions(). AllowTimeTravel(). AddQuery(). - AggregateTypes(keypair.AggregateType, instance.AggregateType). + AggregateTypes(keypair.AggregateType). + EventTypes( + keypair.AddedEventType, + ). + Or(). + AggregateTypes(instance.AggregateType). + EventTypes(instance.InstanceRemovedEventType). Builder(), ) } diff --git a/internal/api/saml/certificate.go b/internal/api/saml/certificate.go index 9e85e5098..d2bf3bf3e 100644 --- a/internal/api/saml/certificate.go +++ b/internal/api/saml/certificate.go @@ -77,20 +77,20 @@ func (p *Storage) getCertificateAndKey(ctx context.Context, usage domain.KeyUsag return p.certificateToCertificateAndKey(selectCertificate(certs.Certificates)) } - var sequence uint64 - if certs.LatestSequence != nil { - sequence = certs.LatestSequence.Sequence + var position float64 + if certs.State != nil { + position = certs.State.Position } - return nil, p.refreshCertificate(ctx, usage, sequence) + return nil, p.refreshCertificate(ctx, usage, position) } func (p *Storage) refreshCertificate( ctx context.Context, usage domain.KeyUsage, - sequence uint64, + position float64, ) error { - ok, err := p.ensureIsLatestCertificate(ctx, sequence) + ok, err := p.ensureIsLatestCertificate(ctx, position) if err != nil { logging.WithError(err).Error("could not ensure latest key") return err @@ -99,20 +99,20 @@ func (p *Storage) refreshCertificate( logging.Warn("view not up to date, retrying later") return err } - err = p.lockAndGenerateCertificateAndKey(ctx, usage, sequence) + err = p.lockAndGenerateCertificateAndKey(ctx, usage) logging.OnError(err).Warn("could not create signing key") return nil } -func (p *Storage) ensureIsLatestCertificate(ctx context.Context, sequence uint64) (bool, error) { +func (p *Storage) ensureIsLatestCertificate(ctx context.Context, position float64) (bool, error) { maxSequence, err := p.getMaxKeySequence(ctx) if err != nil { return false, fmt.Errorf("error retrieving new events: %w", err) } - return sequence == maxSequence, nil + return position >= maxSequence, nil } -func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage domain.KeyUsage, sequence uint64) error { +func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage domain.KeyUsage) error { ctx, cancel := context.WithCancel(ctx) defer cancel() ctx = setSAMLCtx(ctx) @@ -152,12 +152,20 @@ func (p *Storage) lockAndGenerateCertificateAndKey(ctx context.Context, usage do } } -func (p *Storage) getMaxKeySequence(ctx context.Context) (uint64, error) { +func (p *Storage) getMaxKeySequence(ctx context.Context) (float64, error) { return p.eventstore.LatestSequence(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence). ResourceOwner(authz.GetInstance(ctx).InstanceID()). + AwaitOpenTransactions(). AddQuery(). - AggregateTypes(keypair.AggregateType, instance.AggregateType). + AggregateTypes(keypair.AggregateType). + EventTypes( + keypair.AddedEventType, + keypair.AddedCertificateEventType, + ). + Or(). + AggregateTypes(instance.AggregateType). + EventTypes(instance.InstanceRemovedEventType). Builder(), ) } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 8dbf19455..24ef2f15f 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/query" @@ -24,7 +23,6 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" user_model "github.com/zitadel/zitadel/internal/user/model" user_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const unknownUserID = "UNKNOWN" @@ -34,7 +32,6 @@ type AuthRequestRepo struct { Query *query.Queries AuthRequests cache.AuthRequestCache View *view.View - Eventstore v1.Eventstore UserCodeAlg crypto.EncryptionAlgorithm LabelPolicyProvider labelPolicyProvider @@ -69,7 +66,7 @@ type privacyPolicyProvider interface { type userSessionViewProvider interface { UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error) - GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) + GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) } type userViewProvider interface { @@ -93,7 +90,7 @@ type idpUserLinksProvider interface { } type userEventProvider interface { - UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []es_models.EventType) ([]*es_models.Event, error) + UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []eventstore.EventType) ([]eventstore.Event, error) } type userCommandProvider interface { @@ -1436,25 +1433,25 @@ func userSessionsByUserAgentID(ctx context.Context, provider userSessionViewProv } var ( - userSessionEventTypes = []es_models.EventType{ - es_models.EventType(user_repo.UserV1PasswordCheckSucceededType), - es_models.EventType(user_repo.UserV1PasswordCheckFailedType), - es_models.EventType(user_repo.UserV1MFAOTPCheckSucceededType), - es_models.EventType(user_repo.UserV1MFAOTPCheckFailedType), - es_models.EventType(user_repo.UserV1SignedOutType), - es_models.EventType(user_repo.UserLockedType), - es_models.EventType(user_repo.UserDeactivatedType), - es_models.EventType(user_repo.HumanPasswordCheckSucceededType), - es_models.EventType(user_repo.HumanPasswordCheckFailedType), - es_models.EventType(user_repo.UserIDPLoginCheckSucceededType), - es_models.EventType(user_repo.HumanMFAOTPCheckSucceededType), - es_models.EventType(user_repo.HumanMFAOTPCheckFailedType), - es_models.EventType(user_repo.HumanSignedOutType), - es_models.EventType(user_repo.HumanPasswordlessTokenCheckSucceededType), - es_models.EventType(user_repo.HumanPasswordlessTokenCheckFailedType), - es_models.EventType(user_repo.HumanU2FTokenCheckSucceededType), - es_models.EventType(user_repo.HumanU2FTokenCheckFailedType), - es_models.EventType(user_repo.UserRemovedType), + userSessionEventTypes = []eventstore.EventType{ + user_repo.UserV1PasswordCheckSucceededType, + user_repo.UserV1PasswordCheckFailedType, + user_repo.UserV1MFAOTPCheckSucceededType, + user_repo.UserV1MFAOTPCheckFailedType, + user_repo.UserV1SignedOutType, + user_repo.UserLockedType, + user_repo.UserDeactivatedType, + user_repo.HumanPasswordCheckSucceededType, + user_repo.HumanPasswordCheckFailedType, + user_repo.UserIDPLoginCheckSucceededType, + user_repo.HumanMFAOTPCheckSucceededType, + user_repo.HumanMFAOTPCheckFailedType, + user_repo.HumanSignedOutType, + user_repo.HumanPasswordlessTokenCheckSucceededType, + user_repo.HumanPasswordlessTokenCheckFailedType, + user_repo.HumanU2FTokenCheckSucceededType, + user_repo.HumanU2FTokenCheckFailedType, + user_repo.UserRemovedType, } ) @@ -1471,7 +1468,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve Errorf("could not get current sequence for userSessionByIDs") session = &user_view_model.UserSessionView{UserAgentID: agentID, UserID: user.ID} if sequence != nil { - session.Sequence = sequence.CurrentSequence + session.Sequence = sequence.Sequence } } events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence, append(session.EventTypes(), userSessionEventTypes...)) @@ -1481,7 +1478,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve } sessionCopy := *session for _, event := range events { - switch eventstore.EventType(event.Type) { + switch event.Type() { case user_repo.UserV1PasswordCheckSucceededType, user_repo.UserV1PasswordCheckFailedType, user_repo.UserV1MFAOTPCheckSucceededType, diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 7a469e472..95ad95a32 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -15,13 +15,13 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/query" user_repo "github.com/zitadel/zitadel/internal/repository/user" user_model "github.com/zitadel/zitadel/internal/user/model" user_es_model "github.com/zitadel/zitadel/internal/user/repository/eventsourcing/model" user_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) var ( @@ -38,8 +38,8 @@ func (m *mockViewNoUserSession) UserSessionsByAgentID(string, string) ([]*user_v return nil, nil } -func (m *mockViewNoUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return &repository.CurrentSequence{}, nil +func (m *mockViewNoUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) { + return &query.CurrentState{State: query.State{Sequence: 0}}, nil } type mockViewErrUserSession struct{} @@ -52,8 +52,8 @@ func (m *mockViewErrUserSession) UserSessionsByAgentID(string, string) ([]*user_ return nil, errors.ThrowInternal(nil, "id", "internal error") } -func (m *mockViewErrUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return &repository.CurrentSequence{}, nil +func (m *mockViewErrUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) { + return &query.CurrentState{State: query.State{Sequence: 0}}, nil } type mockViewUserSession struct { @@ -95,8 +95,8 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string, string) ([]*user_vie return sessions, nil } -func (m *mockViewUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return &repository.CurrentSequence{}, nil +func (m *mockViewUserSession) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) { + return &query.CurrentState{State: query.State{Sequence: 0}}, nil } type mockViewNoUser struct{} @@ -106,15 +106,18 @@ func (m *mockViewNoUser) UserByID(string, string) (*user_view_model.UserView, er } type mockEventUser struct { - Event *es_models.Event + Event eventstore.Event } -func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []es_models.EventType) ([]*es_models.Event, error) { - events := make([]*es_models.Event, 0) +func (m *mockEventUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []eventstore.EventType) ([]eventstore.Event, error) { if m.Event != nil { - events = append(events, m.Event) + return []eventstore.Event{m.Event}, nil } - return events, nil + return nil, nil +} + +func (m *mockEventUser) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*query.CurrentState, error) { + return &query.CurrentState{State: query.State{Sequence: 0}}, nil } func (m *mockEventUser) BulkAddExternalIDPs(ctx context.Context, userID string, externalIDPs []*user_model.ExternalIDP) error { @@ -123,7 +126,7 @@ func (m *mockEventUser) BulkAddExternalIDPs(ctx context.Context, userID string, type mockEventErrUser struct{} -func (m *mockEventErrUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []es_models.EventType) ([]*es_models.Event, error) { +func (m *mockEventErrUser) UserEventsByID(ctx context.Context, id string, sequence uint64, types []eventstore.EventType) ([]eventstore.Event, error) { return nil, errors.ThrowInternal(nil, "id", "internal error") } @@ -672,7 +675,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserDeactivatedType), + Typ: user_repo.UserDeactivatedType, }, }, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, @@ -694,7 +697,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserLockedType), + Typ: user_repo.UserLockedType, }, }, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, @@ -2099,7 +2102,7 @@ func Test_userSessionByIDs(t *testing.T) { eventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserV1MFAOTPCheckSucceededType), + Typ: user_repo.UserV1MFAOTPCheckSucceededType, CreationDate: testNow, }, }, @@ -2122,7 +2125,7 @@ func Test_userSessionByIDs(t *testing.T) { eventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserV1MFAOTPCheckSucceededType), + Typ: user_repo.UserV1MFAOTPCheckSucceededType, CreationDate: testNow, Data: func() []byte { data, _ := json.Marshal(&user_es_model.AuthRequest{UserAgentID: "otherID"}) @@ -2149,7 +2152,7 @@ func Test_userSessionByIDs(t *testing.T) { eventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserV1MFAOTPCheckSucceededType), + Typ: user_repo.UserV1MFAOTPCheckSucceededType, CreationDate: testNow, Data: func() []byte { data, _ := json.Marshal(&user_es_model.AuthRequest{UserAgentID: "agentID"}) @@ -2176,7 +2179,7 @@ func Test_userSessionByIDs(t *testing.T) { eventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserRemovedType), + Typ: user_repo.UserRemovedType, }, }, }, @@ -2243,14 +2246,7 @@ func Test_userByID(t *testing.T) { viewProvider: &mockViewUser{ PasswordChangeRequired: true, }, - eventProvider: &mockEventUser{ - &es_models.Event{ - AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserV1PasswordChangedType), - CreationDate: testNow, - Data: nil, - }, - }, + eventProvider: &mockEventErrUser{}, }, &user_model.UserView{ State: user_model.UserStateActive, @@ -2272,7 +2268,7 @@ func Test_userByID(t *testing.T) { eventProvider: &mockEventUser{ &es_models.Event{ AggregateType: user_repo.AggregateType, - Type: es_models.EventType(user_repo.UserV1PasswordChangedType), + Typ: user_repo.UserV1PasswordChangedType, CreationDate: testNow, Data: func() []byte { data, _ := json.Marshal(user_es_model.Password{ChangeRequired: false, Secret: &crypto.CryptoValue{}}) diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index fa15bfc2c..938f0d27c 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -7,7 +7,7 @@ import ( auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/domain" - eventstore "github.com/zitadel/zitadel/internal/eventstore/v1" + "github.com/zitadel/zitadel/internal/eventstore" iam_model "github.com/zitadel/zitadel/internal/iam/model" iam_view_model "github.com/zitadel/zitadel/internal/iam/repository/view/model" "github.com/zitadel/zitadel/internal/query" @@ -16,7 +16,7 @@ import ( type OrgRepository struct { SearchLimit uint64 - Eventstore eventstore.Eventstore + Eventstore *eventstore.Eventstore View *auth_view.View SystemDefaults systemdefaults.SystemDefaults Query *query.Queries diff --git a/internal/auth/repository/eventsourcing/eventstore/refresh_token.go b/internal/auth/repository/eventsourcing/eventstore/refresh_token.go index 7d0c1e7d7..39c5e441d 100644 --- a/internal/auth/repository/eventsourcing/eventstore/refresh_token.go +++ b/internal/auth/repository/eventsourcing/eventstore/refresh_token.go @@ -11,8 +11,7 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/telemetry/tracing" usr_model "github.com/zitadel/zitadel/internal/user/model" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" @@ -20,7 +19,7 @@ import ( ) type RefreshTokenRepo struct { - Eventstore v1.Eventstore + Eventstore *eventstore.Eventstore View *view.View SearchLimit uint64 KeyAlgorithm crypto.EncryptionAlgorithm @@ -48,7 +47,7 @@ func (r *RefreshTokenRepo) RefreshTokenByID(ctx context.Context, tokenID, userID return nil, viewErr } if errors.IsNotFound(viewErr) { - sequence, err := r.View.GetLatestRefreshTokenSequence(ctx, instanceID) + sequence, err := r.View.GetLatestRefreshTokenSequence(ctx) logging.WithFields("instanceID", instanceID, "userID", userID, "tokenID", tokenID). OnError(err). Errorf("could not get current sequence for RefreshTokenByID") @@ -58,7 +57,7 @@ func (r *RefreshTokenRepo) RefreshTokenByID(ctx context.Context, tokenID, userID tokenView.UserID = userID tokenView.InstanceID = instanceID if sequence != nil { - tokenView.Sequence = sequence.CurrentSequence + tokenView.Sequence = sequence.Sequence } } @@ -89,7 +88,7 @@ func (r *RefreshTokenRepo) SearchMyRefreshTokens(ctx context.Context, userID str if err != nil { return nil, err } - sequence, err := r.View.GetLatestRefreshTokenSequence(ctx, authz.GetInstance(ctx).InstanceID()) + sequence, err := r.View.GetLatestRefreshTokenSequence(ctx) logging.Log("EVENT-GBdn4").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest refresh token sequence") request.Queries = append(request.Queries, &usr_model.RefreshTokenSearchQuery{Key: usr_model.RefreshTokenSearchKeyUserID, Method: domain.SearchMethodEquals, Value: userID}) tokens, count, err := r.View.SearchRefreshTokens(request) @@ -100,16 +99,16 @@ func (r *RefreshTokenRepo) SearchMyRefreshTokens(ctx context.Context, userID str Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Sequence: sequence.CurrentSequence, - Timestamp: sequence.LastSuccessfulSpoolerRun, + Sequence: sequence.Sequence, + Timestamp: sequence.LastRun, Result: model.RefreshTokenViewsToModel(tokens), }, nil } -func (r *RefreshTokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { +func (r *RefreshTokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []eventstore.EventType) ([]eventstore.Event, error) { query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } - return r.Eventstore.FilterEvents(ctx, query) + return r.Eventstore.Filter(ctx, query) } diff --git a/internal/auth/repository/eventsourcing/eventstore/token.go b/internal/auth/repository/eventsourcing/eventstore/token.go index 151c25e6b..b1af743a7 100644 --- a/internal/auth/repository/eventsourcing/eventstore/token.go +++ b/internal/auth/repository/eventsourcing/eventstore/token.go @@ -9,8 +9,7 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/errors" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/telemetry/tracing" usr_model "github.com/zitadel/zitadel/internal/user/model" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" @@ -18,21 +17,10 @@ import ( ) type TokenRepo struct { - Eventstore v1.Eventstore + Eventstore *eventstore.Eventstore View *view.View } -func (repo *TokenRepo) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) { - token, err := repo.TokenByIDs(ctx, userID, tokenID) - if err == nil { - return token.Expiration.After(time.Now().UTC()), nil - } - if errors.IsNotFound(err) { - return false, nil - } - return false, err -} - func (repo *TokenRepo) TokenByIDs(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error) { instanceID := authz.GetInstance(ctx).InstanceID() @@ -51,7 +39,7 @@ func (repo *TokenRepo) TokenByIDs(ctx context.Context, userID, tokenID string) ( token.UserID = userID token.InstanceID = instanceID if sequence != nil { - token.Sequence = sequence.CurrentSequence + token.Sequence = sequence.Sequence } } @@ -77,10 +65,10 @@ func (repo *TokenRepo) TokenByIDs(ctx context.Context, userID, tokenID string) ( return model.TokenViewToModel(token), nil } -func (r *TokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { +func (r *TokenRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []eventstore.EventType) ([]eventstore.Event, error) { query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } - return r.Eventstore.FilterEvents(ctx, query) + return r.Eventstore.Filter(ctx, query) } diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index cebf0d474..288527dce 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -7,15 +7,14 @@ import ( "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/domain" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" ) type UserRepo struct { SearchLimit uint64 - Eventstore v1.Eventstore + Eventstore *eventstore.Eventstore View *view.View Query *query.Queries SystemDefaults systemdefaults.SystemDefaults @@ -39,14 +38,14 @@ func (repo *UserRepo) UserSessionUserIDsByAgentID(ctx context.Context, agentID s return userIDs, nil } -func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { +func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64, eventTypes []eventstore.EventType) ([]eventstore.Event, error) { return repo.getUserEvents(ctx, id, sequence, eventTypes) } -func (r *UserRepo) getUserEvents(ctx context.Context, userID string, sequence uint64, eventTypes []models.EventType) ([]*models.Event, error) { +func (r *UserRepo) getUserEvents(ctx context.Context, userID string, sequence uint64, eventTypes []eventstore.EventType) ([]eventstore.Event, error) { query, err := usr_view.UserByIDQuery(userID, authz.GetInstance(ctx).InstanceID(), sequence, eventTypes) if err != nil { return nil, err } - return r.Eventstore.FilterEvents(ctx, query) + return r.Eventstore.Filter(ctx, query) } diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index 3f0baecdb..e9a468785 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -4,84 +4,68 @@ import ( "context" "time" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" + handler2 "github.com/zitadel/zitadel/internal/eventstore/handler/v2" query2 "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/view/repository" ) -type Configs map[string]*Config - type Config struct { + Client *database.DB + Eventstore *eventstore.Eventstore + + BulkLimit uint64 + FailureCountUntilSkip uint64 + HandleActiveInstances time.Duration + TransactionDuration time.Duration + Handlers map[string]*ConfigOverwrites +} + +type ConfigOverwrites struct { MinimumCycleDuration time.Duration } -type handler struct { - view *view.View - bulkLimit uint64 - cycleDuration time.Duration - errorCountUntilSkip uint64 +func Register(ctx context.Context, configs Config, view *view.View, queries *query2.Queries) { + newUser(ctx, + configs.overwrite("User"), + view, + queries, + ).Start(ctx) - es v1.Eventstore + newUserSession(ctx, + configs.overwrite("UserSession"), + view, + queries, + ).Start(ctx) + + newToken(ctx, + configs.overwrite("Token"), + view, + ).Start(ctx) + + newRefreshToken(ctx, + configs.overwrite("RefreshToken"), + view, + ).Start(ctx) } -func (h *handler) Eventstore() v1.Eventstore { - return h.es -} - -func Register(ctx context.Context, configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, queries *query2.Queries) []query.Handler { - return []query.Handler{ - newUser(ctx, - handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, queries), - newUserSession(ctx, - handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount, es}, queries), - newToken(ctx, - handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount, es}), - newRefreshToken(ctx, handler{view, bulkLimit, configs.cycleDuration("RefreshToken"), errorCount, es}), +func (config Config) overwrite(viewModel string) handler2.Config { + c := handler2.Config{ + Client: config.Client, + Eventstore: config.Eventstore, + BulkLimit: uint16(config.BulkLimit), + RequeueEvery: 3 * time.Minute, + HandleActiveInstances: config.HandleActiveInstances, + MaxFailureCount: uint8(config.FailureCountUntilSkip), + TransactionDuration: config.TransactionDuration, } -} - -func (configs Configs) cycleDuration(viewModel string) time.Duration { - c, ok := configs[viewModel] + overwrite, ok := config.Handlers[viewModel] if !ok { - return 3 * time.Minute + return c } - return c.MinimumCycleDuration -} - -func (h *handler) MinimumCycleDuration() time.Duration { - return h.cycleDuration -} - -func (h *handler) LockDuration() time.Duration { - return h.cycleDuration / 3 -} - -func (h *handler) QueryLimit() uint64 { - return h.bulkLimit -} - -func withInstanceID(ctx context.Context, instanceID string) context.Context { - return authz.WithInstanceID(ctx, instanceID) -} - -func newSearchQuery(sequences []*repository.CurrentSequence, aggregateTypes []models.AggregateType, instanceIDs []string) *models.SearchQuery { - searchQuery := models.NewSearchQuery() - for _, instanceID := range instanceIDs { - var seq uint64 - for _, sequence := range sequences { - if sequence.InstanceID == instanceID { - seq = sequence.CurrentSequence - break - } - } - searchQuery.AddQuery(). - AggregateTypeFilter(aggregateTypes...). - LatestSequenceFilter(seq). - InstanceIDFilter(instanceID) + if overwrite.MinimumCycleDuration > 0 { + c.RequeueEvery = overwrite.MinimumCycleDuration } - return searchQuery + return c } diff --git a/internal/auth/repository/eventsourcing/handler/refresh_token.go b/internal/auth/repository/eventsourcing/handler/refresh_token.go index 51f158b68..5a1b59d49 100644 --- a/internal/auth/repository/eventsourcing/handler/refresh_token.go +++ b/internal/auth/repository/eventsourcing/handler/refresh_token.go @@ -2,19 +2,15 @@ package handler import ( "context" - "encoding/json" "github.com/zitadel/logging" + auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" - "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" view_model "github.com/zitadel/zitadel/internal/user/repository/view/model" ) @@ -23,110 +19,129 @@ const ( refreshTokenTable = "auth.refresh_tokens" ) +var _ handler.Projection = (*RefreshToken)(nil) + type RefreshToken struct { - handler - subscription *v1.Subscription + view *auth_view.View } func newRefreshToken( ctx context.Context, - handler handler, -) *RefreshToken { - h := &RefreshToken{ - handler: handler, - } - - h.subscribe(ctx) - - return h + config handler.Config, + view *auth_view.View, +) *handler.Handler { + return handler.NewHandler( + ctx, + &config, + &RefreshToken{ + view: view, + }, + ) } -func (t *RefreshToken) subscribe(ctx context.Context) { - t.subscription = t.es.Subscribe(t.AggregateTypes()...) - go func() { - for event := range t.subscription.Events { - query.ReduceEvent(ctx, t, event) - } - }() -} - -func (t *RefreshToken) ViewModel() string { +// Name implements [handler.Projection] +func (*RefreshToken) Name() string { return refreshTokenTable } -func (t *RefreshToken) Subscription() *v1.Subscription { - return t.subscription -} - -func (t *RefreshToken) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{user.AggregateType, project.AggregateType, instance.AggregateType} -} - -func (t *RefreshToken) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - sequence, err := t.view.GetLatestRefreshTokenSequence(ctx, instanceID) - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (t *RefreshToken) EventQuery(ctx context.Context, instanceIDs []string) (*es_models.SearchQuery, error) { - sequences, err := t.view.GetLatestRefreshTokenSequences(ctx, instanceIDs) - if err != nil { - return nil, err - } - return newSearchQuery(sequences, t.AggregateTypes(), instanceIDs), nil -} - -func (t *RefreshToken) Reduce(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { - case user.HumanRefreshTokenAddedType: - token := new(view_model.RefreshTokenView) - err := token.AppendEvent(event) - if err != nil { - return err - } - return t.view.PutRefreshToken(token, event) - case user.HumanRefreshTokenRenewedType: - e := new(user.HumanRefreshTokenRenewedEvent) - if err := json.Unmarshal(event.Data, e); err != nil { - logging.WithError(err).Error("could not unmarshal event data") - return caos_errs.ThrowInternal(nil, "MODEL-BHn75", "could not unmarshal data") - } - token, err := t.view.RefreshTokenByID(e.TokenID, event.InstanceID) - if err != nil { - return err - } - err = token.AppendEvent(event) - if err != nil { - return err - } - return t.view.PutRefreshToken(token, event) - case user.HumanRefreshTokenRemovedType: - e := new(user.HumanRefreshTokenRemovedEvent) - if err := json.Unmarshal(event.Data, e); err != nil { - logging.WithError(err).Error("could not unmarshal event data") - return caos_errs.ThrowInternal(nil, "MODEL-Bz653", "could not unmarshal data") - } - return t.view.DeleteRefreshToken(e.TokenID, event.InstanceID, event) - case user.UserLockedType, - user.UserDeactivatedType, - user.UserRemovedType: - return t.view.DeleteUserRefreshTokens(event.AggregateID, event.InstanceID, event) - case instance.InstanceRemovedEventType: - return t.view.DeleteInstanceRefreshTokens(event) - case org.OrgRemovedEventType: - return t.view.DeleteOrgRefreshTokens(event) - default: - return t.view.ProcessedRefreshTokenSequence(event) +// Reducers implements [handler.Projection] +func (t *RefreshToken) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: user.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: user.HumanRefreshTokenAddedType, + Reduce: t.Reduce, + }, + { + Event: user.HumanRefreshTokenRenewedType, + Reduce: t.Reduce, + }, + { + Event: user.HumanRefreshTokenRemovedType, + Reduce: t.Reduce, + }, + { + Event: user.UserLockedType, + Reduce: t.Reduce, + }, + { + Event: user.UserDeactivatedType, + Reduce: t.Reduce, + }, + { + Event: user.UserRemovedType, + Reduce: t.Reduce, + }, + }, + }, + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.InstanceRemovedEventType, + Reduce: t.Reduce, + }, + }, + }, + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.OrgRemovedEventType, + Reduce: t.Reduce, + }, + }, + }, } } -func (t *RefreshToken) OnError(event *es_models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in token handler") - return spooler.HandleError(event, err, t.view.GetLatestRefreshTokenFailedEvent, t.view.ProcessedRefreshTokenFailedEvent, t.view.ProcessedRefreshTokenSequence, t.errorCountUntilSkip) -} +func (t *RefreshToken) Reduce(event eventstore.Event) (_ *handler.Statement, err error) { + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + switch event.Type() { + case user.HumanRefreshTokenAddedType: + token := new(view_model.RefreshTokenView) + err := token.AppendEvent(event) + if err != nil { + return err + } -func (t *RefreshToken) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(t.view.UpdateRefreshTokenSpoolerRunTimestamp, instanceIDs) + return t.view.PutRefreshToken(token) + case user.HumanRefreshTokenRenewedType: + e := new(user.HumanRefreshTokenRenewedEvent) + if err := event.Unmarshal(e); err != nil { + logging.WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(nil, "MODEL-BHn75", "could not unmarshal data") + } + token, err := t.view.RefreshTokenByID(e.TokenID, event.Aggregate().InstanceID) + if err != nil { + return err + } + err = token.AppendEvent(event) + if err != nil { + return err + } + return t.view.PutRefreshToken(token) + case user.HumanRefreshTokenRemovedType: + e := new(user.HumanRefreshTokenRemovedEvent) + if err := event.Unmarshal(e); err != nil { + logging.WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(nil, "MODEL-Bz653", "could not unmarshal data") + } + return t.view.DeleteRefreshToken(e.TokenID, event.Aggregate().InstanceID) + case user.UserLockedType, + user.UserDeactivatedType, + user.UserRemovedType: + + return t.view.DeleteUserRefreshTokens(event.Aggregate().ID, event.Aggregate().InstanceID) + case instance.InstanceRemovedEventType: + + return t.view.DeleteInstanceRefreshTokens(event.Aggregate().InstanceID) + case org.OrgRemovedEventType: + return t.view.DeleteOrgRefreshTokens(event) + default: + return nil + } + }), nil } diff --git a/internal/auth/repository/eventsourcing/handler/token.go b/internal/auth/repository/eventsourcing/handler/token.go index 7bb5bd321..04cc19f50 100644 --- a/internal/auth/repository/eventsourcing/handler/token.go +++ b/internal/auth/repository/eventsourcing/handler/token.go @@ -2,17 +2,14 @@ package handler import ( "context" - "encoding/json" "github.com/zitadel/logging" + auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - es_sdk "github.com/zitadel/zitadel/internal/eventstore/v1/sdk" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" proj_model "github.com/zitadel/zitadel/internal/project/model" project_es_model "github.com/zitadel/zitadel/internal/project/repository/eventsourcing/model" proj_view "github.com/zitadel/zitadel/internal/project/repository/view" @@ -20,7 +17,6 @@ import ( "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" - user_repo "github.com/zitadel/zitadel/internal/repository/user" view_model "github.com/zitadel/zitadel/internal/user/repository/view/model" ) @@ -28,189 +24,258 @@ const ( tokenTable = "auth.tokens" ) +var _ handler.Projection = (*Token)(nil) + type Token struct { - handler - subscription *v1.Subscription + view *auth_view.View + es handler.EventStore } func newToken( ctx context.Context, - handler handler, -) *Token { - h := &Token{ - handler: handler, - } - - h.subscribe(ctx) - - return h + config handler.Config, + view *auth_view.View, +) *handler.Handler { + return handler.NewHandler( + ctx, + &config, + &Token{ + view: view, + es: config.Eventstore, + }, + ) } -func (t *Token) subscribe(ctx context.Context) { - t.subscription = t.es.Subscribe(t.AggregateTypes()...) - go func() { - for event := range t.subscription.Events { - query.ReduceEvent(ctx, t, event) - } - }() -} - -func (t *Token) ViewModel() string { +// Name implements [handler.Projection] +func (*Token) Name() string { return tokenTable } -func (t *Token) Subscription() *v1.Subscription { - return t.subscription -} - -func (_ *Token) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{user.AggregateType, project.AggregateType, instance.AggregateType} -} - -func (t *Token) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - sequence, err := t.view.GetLatestTokenSequence(ctx, instanceID) - if err != nil { - return 0, err +// Reducers implements [handler.Projection] +func (t *Token) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: user.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: user.PersonalAccessTokenAddedType, + Reduce: t.Reduce, + }, + { + Event: user.UserTokenAddedType, + Reduce: t.Reduce, + }, + { + Event: user.UserV1ProfileChangedType, + Reduce: t.Reduce, + }, + { + Event: user.HumanProfileChangedType, + Reduce: t.Reduce, + }, + { + Event: user.UserV1SignedOutType, + Reduce: t.Reduce, + }, + { + Event: user.HumanSignedOutType, + Reduce: t.Reduce, + }, + { + Event: user.UserLockedType, + Reduce: t.Reduce, + }, + { + Event: user.UserDeactivatedType, + Reduce: t.Reduce, + }, + { + Event: user.UserRemovedType, + Reduce: t.Reduce, + }, + { + Event: user.UserTokenRemovedType, + Reduce: t.Reduce, + }, + { + Event: user.PersonalAccessTokenRemovedType, + Reduce: t.Reduce, + }, + { + Event: user.HumanRefreshTokenRemovedType, + Reduce: t.Reduce, + }, + }, + }, + { + Aggregate: project.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: project.ApplicationDeactivatedType, + Reduce: t.Reduce, + }, + { + Event: project.ApplicationRemovedType, + Reduce: t.Reduce, + }, + { + Event: project.ProjectDeactivatedType, + Reduce: t.Reduce, + }, + { + Event: project.ProjectRemovedType, + Reduce: t.Reduce, + }, + }, + }, + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.OrgRemovedEventType, + Reduce: t.Reduce, + }, + }, + }, + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.InstanceRemovedEventType, + Reduce: t.Reduce, + }, + }, + }, } - return sequence.CurrentSequence, nil } -func (t *Token) EventQuery(ctx context.Context, instanceIDs []string) (*es_models.SearchQuery, error) { - sequences, err := t.view.GetLatestTokenSequences(ctx, instanceIDs) - if err != nil { - return nil, err - } - return newSearchQuery(sequences, t.AggregateTypes(), instanceIDs), nil -} - -func (t *Token) Reduce(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { - case user.UserTokenAddedType, - user_repo.PersonalAccessTokenAddedType: - token := new(view_model.TokenView) - err := token.AppendEvent(event) - if err != nil { - return err - } - return t.view.PutToken(token, event) - case user.UserV1ProfileChangedType, - user.HumanProfileChangedType: - user := new(view_model.UserView) - err := user.AppendEvent(event) - if err != nil { - return err - } - tokens, err := t.view.TokensByUserID(event.AggregateID, event.InstanceID) - if err != nil { - return err - } - for _, token := range tokens { - token.PreferredLanguage = user.PreferredLanguage - } - return t.view.PutTokens(tokens, event) - case user.UserV1SignedOutType, - user.HumanSignedOutType: - id, err := agentIDFromSession(event) - if err != nil { - return err - } - return t.view.DeleteSessionTokens(id, event.AggregateID, event.InstanceID, event) - case user.UserLockedType, - user.UserDeactivatedType, - user.UserRemovedType: - return t.view.DeleteUserTokens(event.AggregateID, event.InstanceID, event) - case user_repo.UserTokenRemovedType, - user_repo.PersonalAccessTokenRemovedType: - id, err := tokenIDFromRemovedEvent(event) - if err != nil { - return err - } - return t.view.DeleteToken(id, event.InstanceID, event) - case user_repo.HumanRefreshTokenRemovedType: - id, err := refreshTokenIDFromRemovedEvent(event) - if err != nil { - return err - } - return t.view.DeleteTokensFromRefreshToken(id, event.InstanceID, event) - case project.ApplicationDeactivatedType, - project.ApplicationRemovedType: - application, err := applicationFromSession(event) - if err != nil { - return err - } - return t.view.DeleteApplicationTokens(event, application.AppID) - case project.ProjectDeactivatedType, - project.ProjectRemovedType: - project, err := t.getProjectByID(context.Background(), event.AggregateID, event.InstanceID) - if err != nil { - return err - } - clientIDs := make([]string, 0, len(project.Applications)) - for _, app := range project.Applications { - if app.OIDCConfig != nil { - clientIDs = append(clientIDs, app.OIDCConfig.ClientID) +func (t *Token) Reduce(event eventstore.Event) (_ *handler.Statement, err error) { //nolint:gocognit + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + switch event.Type() { + case user.UserTokenAddedType, + user.PersonalAccessTokenAddedType: + token := new(view_model.TokenView) + err := token.AppendEvent(event) + if err != nil { + return err } + return t.view.PutToken(token) + case user.UserV1ProfileChangedType, + user.HumanProfileChangedType: + user := new(view_model.UserView) + err := user.AppendEvent(event) + if err != nil { + return err + } + tokens, err := t.view.TokensByUserID(event.Aggregate().ID, event.Aggregate().InstanceID) + if err != nil { + return err + } + for _, token := range tokens { + token.PreferredLanguage = user.PreferredLanguage + } + return t.view.PutTokens(tokens) + case user.UserV1SignedOutType, + user.HumanSignedOutType: + id, err := agentIDFromSession(event) + if err != nil { + return err + } + + return t.view.DeleteSessionTokens(id, event) + case user.UserLockedType, + user.UserDeactivatedType, + user.UserRemovedType: + + return t.view.DeleteUserTokens(event) + case user.UserTokenRemovedType, + user.PersonalAccessTokenRemovedType: + id, err := tokenIDFromRemovedEvent(event) + if err != nil { + return err + } + + return t.view.DeleteToken(id, event.Aggregate().InstanceID) + case user.HumanRefreshTokenRemovedType: + id, err := refreshTokenIDFromRemovedEvent(event) + if err != nil { + return err + } + + return t.view.DeleteTokensFromRefreshToken(id, event.Aggregate().InstanceID) + case project.ApplicationDeactivatedType, + project.ApplicationRemovedType: + application, err := applicationFromSession(event) + if err != nil { + return err + } + + return t.view.DeleteApplicationTokens(event, application.AppID) + case project.ProjectDeactivatedType, + project.ProjectRemovedType: + project, err := t.getProjectByID(context.Background(), event.Aggregate().ID, event.Aggregate().InstanceID) + if err != nil { + return err + } + applicationIDs := make([]string, 0, len(project.Applications)) + for _, app := range project.Applications { + if app.OIDCConfig != nil { + applicationIDs = append(applicationIDs, app.OIDCConfig.ClientID) + } + } + + return t.view.DeleteApplicationTokens(event, applicationIDs...) + case instance.InstanceRemovedEventType: + return t.view.DeleteInstanceTokens(event) + case org.OrgRemovedEventType: + // deletes all tokens including PATs, which is expected for now + // if there is an undo of the org deletion in the future, + // we will need to have a look on how to handle the deleted PATs + return t.view.DeleteOrgTokens(event) + default: + return nil } - return t.view.DeleteApplicationTokens(event, clientIDs...) - case instance.InstanceRemovedEventType: - return t.view.DeleteInstanceTokens(event) - case org.OrgRemovedEventType: - // deletes all tokens including PATs, which is expected for now - // if there is an undo of the org deletion in the future, - // we will need to have a look on how to handle the deleted PATs - return t.view.DeleteOrgTokens(event) - default: - return t.view.ProcessedTokenSequence(event) - } + }), nil } -func (t *Token) OnError(event *es_models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in token handler") - return spooler.HandleError(event, err, t.view.GetLatestTokenFailedEvent, t.view.ProcessedTokenFailedEvent, t.view.ProcessedTokenSequence, t.errorCountUntilSkip) -} - -func agentIDFromSession(event *es_models.Event) (string, error) { +func agentIDFromSession(event eventstore.Event) (string, error) { session := make(map[string]interface{}) - if err := json.Unmarshal(event.Data, &session); err != nil { + if err := event.Unmarshal(&session); err != nil { logging.WithError(err).Error("could not unmarshal event data") return "", caos_errs.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data") } return session["userAgentID"].(string), nil } -func applicationFromSession(event *es_models.Event) (*project_es_model.Application, error) { +func applicationFromSession(event eventstore.Event) (*project_es_model.Application, error) { application := new(project_es_model.Application) - if err := json.Unmarshal(event.Data, &application); err != nil { + if err := event.Unmarshal(application); err != nil { logging.WithError(err).Error("could not unmarshal event data") return nil, caos_errs.ThrowInternal(nil, "MODEL-Hrw1q", "could not unmarshal data") } return application, nil } -func tokenIDFromRemovedEvent(event *es_models.Event) (string, error) { +func tokenIDFromRemovedEvent(event eventstore.Event) (string, error) { removed := make(map[string]interface{}) - if err := json.Unmarshal(event.Data, &removed); err != nil { + if err := event.Unmarshal(&removed); err != nil { logging.WithError(err).Error("could not unmarshal event data") return "", caos_errs.ThrowInternal(nil, "MODEL-Sff32", "could not unmarshal data") } return removed["tokenId"].(string), nil } -func refreshTokenIDFromRemovedEvent(event *es_models.Event) (string, error) { +func refreshTokenIDFromRemovedEvent(event eventstore.Event) (string, error) { removed := make(map[string]interface{}) - if err := json.Unmarshal(event.Data, &removed); err != nil { + if err := event.Unmarshal(&removed); err != nil { logging.WithError(err).Error("could not unmarshal event data") return "", caos_errs.ThrowInternal(nil, "MODEL-Dfb3w", "could not unmarshal data") } return removed["tokenId"].(string), nil } -func (t *Token) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(t.view.UpdateTokenSpoolerRunTimestamp, instanceIDs) -} - func (t *Token) getProjectByID(ctx context.Context, projID, instanceID string) (*proj_model.Project, error) { - projectQuery, err := proj_view.ProjectByIDQuery(projID, instanceID, 0) + query, err := proj_view.ProjectByIDQuery(projID, instanceID, 0) if err != nil { return nil, err } @@ -219,13 +284,16 @@ func (t *Token) getProjectByID(ctx context.Context, projID, instanceID string) ( AggregateID: projID, }, } - err = es_sdk.Filter(ctx, t.Eventstore().FilterEvents, esProject.AppendEvents, projectQuery) - if err != nil && !caos_errs.IsNotFound(err) { + events, err := t.es.Filter(ctx, query) + if err != nil { return nil, err } + if err = esProject.AppendEvents(events...); err != nil { + return nil, err + } + if esProject.Sequence == 0 { return nil, caos_errs.ThrowNotFound(nil, "EVENT-Dsdw2", "Errors.Project.NotFound") } - return project_es_model.ProjectToModel(esProject), nil } diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go index 4224a0ca6..09dfe6f96 100644 --- a/internal/auth/repository/eventsourcing/handler/user.go +++ b/internal/auth/repository/eventsourcing/handler/user.go @@ -3,18 +3,15 @@ package handler import ( "context" - "github.com/zitadel/logging" - + "github.com/zitadel/zitadel/internal/api/authz" + auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - es_sdk "github.com/zitadel/zitadel/internal/eventstore/v1/sdk" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" org_model "github.com/zitadel/zitadel/internal/org/model" org_es_model "github.com/zitadel/zitadel/internal/org/repository/eventsourcing/model" - "github.com/zitadel/zitadel/internal/org/repository/view" + org_view "github.com/zitadel/zitadel/internal/org/repository/view" query2 "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -28,202 +25,405 @@ const ( ) type User struct { - handler - subscription *v1.Subscription - queries *query2.Queries + view *auth_view.View + queries *query2.Queries + es handler.EventStore } +var _ handler.Projection = (*User)(nil) + func newUser( ctx context.Context, - handler handler, + config handler.Config, + view *auth_view.View, queries *query2.Queries, -) *User { - h := &User{ - handler: handler, - queries: queries, - } - - h.subscribe(ctx) - - return h +) *handler.Handler { + return handler.NewHandler( + ctx, + &config, + &User{ + view: view, + queries: queries, + es: config.Eventstore, + }, + ) } -func (u *User) subscribe(ctx context.Context) { - u.subscription = u.es.Subscribe(u.AggregateTypes()...) - go func() { - for event := range u.subscription.Events { - query.ReduceEvent(ctx, u, event) - } - }() -} - -func (u *User) ViewModel() string { +func (*User) Name() string { return userTable } - -func (u *User) Subscription() *v1.Subscription { - return u.subscription -} -func (_ *User) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{user_repo.AggregateType, org.AggregateType, instance.AggregateType} -} - -func (u *User) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - sequence, err := u.view.GetLatestUserSequence(ctx, instanceID) - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (u *User) EventQuery(ctx context.Context, instanceIDs []string) (*es_models.SearchQuery, error) { - sequences, err := u.view.GetLatestUserSequences(ctx, instanceIDs) - if err != nil { - return nil, err - } - return newSearchQuery(sequences, u.AggregateTypes(), instanceIDs), nil -} - -func (u *User) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case user_repo.AggregateType: - return u.ProcessUser(event) - case org.AggregateType: - return u.ProcessOrg(event) - case instance.AggregateType: - return u.ProcessInstance(event) - default: - return nil +func (u *User) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: user_repo.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: user_repo.HumanOTPSMSAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanOTPSMSRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanOTPEmailAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanOTPEmailRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.MachineAddedEventType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1AddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1RegisteredType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanRegisteredType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1ProfileChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1EmailChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1EmailVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1PhoneChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1PhoneVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1PhoneRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1AddressChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserDeactivatedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserReactivatedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserLockedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserUnlockedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1MFAOTPAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1MFAOTPVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1MFAOTPRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1MFAInitSkippedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1PasswordChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanProfileChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanEmailChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanEmailVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanAvatarAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanAvatarRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPhoneChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPhoneVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPhoneRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanAddressChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanMFAOTPAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanMFAOTPVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanMFAOTPRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanU2FTokenAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanU2FTokenVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanU2FTokenRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordlessTokenAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordlessTokenVerifiedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordlessTokenRemovedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanMFAInitSkippedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.MachineChangedEventType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanInitialCodeAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1InitialCodeAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserV1InitializedCheckSucceededType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanInitializedCheckSucceededType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordlessInitCodeAddedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.HumanPasswordlessInitCodeRequestedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserDomainClaimedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserUserNameChangedType, + Reduce: u.ProcessUser, + }, + { + Event: user_repo.UserRemovedType, + Reduce: u.ProcessUser, + }, + }, + }, + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.OrgDomainVerifiedEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.OrgDomainRemovedEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.DomainPolicyAddedEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.DomainPolicyChangedEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.DomainPolicyRemovedEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.OrgDomainPrimarySetEventType, + Reduce: u.ProcessOrg, + }, + { + Event: org.OrgRemovedEventType, + Reduce: u.ProcessOrg, + }, + }, + }, + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.InstanceRemovedEventType, + Reduce: u.ProcessInstance, + }, + }, + }, } } -func (u *User) ProcessUser(event *es_models.Event) (err error) { - user := new(view_model.UserView) - switch eventstore.EventType(event.Type) { - case user_repo.UserV1AddedType, - user_repo.MachineAddedEventType, - user_repo.HumanAddedType, - user_repo.UserV1RegisteredType, - user_repo.HumanRegisteredType: - err = user.AppendEvent(event) - if err != nil { - return err - } - err = u.fillLoginNames(user) - case user_repo.UserV1ProfileChangedType, - user_repo.UserV1EmailChangedType, - user_repo.UserV1EmailVerifiedType, - user_repo.UserV1PhoneChangedType, - user_repo.UserV1PhoneVerifiedType, - user_repo.UserV1PhoneRemovedType, - user_repo.UserV1AddressChangedType, - user_repo.UserDeactivatedType, - user_repo.UserReactivatedType, - user_repo.UserLockedType, - user_repo.UserUnlockedType, - user_repo.UserV1MFAOTPAddedType, - user_repo.UserV1MFAOTPVerifiedType, - user_repo.UserV1MFAOTPRemovedType, - user_repo.UserV1MFAInitSkippedType, - user_repo.UserV1PasswordChangedType, - user_repo.HumanProfileChangedType, - user_repo.HumanEmailChangedType, - user_repo.HumanEmailVerifiedType, - user_repo.HumanAvatarAddedType, - user_repo.HumanAvatarRemovedType, - user_repo.HumanPhoneChangedType, - user_repo.HumanPhoneVerifiedType, - user_repo.HumanPhoneRemovedType, - user_repo.HumanAddressChangedType, - user_repo.HumanMFAOTPAddedType, - user_repo.HumanMFAOTPVerifiedType, - user_repo.HumanMFAOTPRemovedType, - user_repo.HumanOTPSMSAddedType, - user_repo.HumanOTPSMSRemovedType, - user_repo.HumanOTPEmailAddedType, - user_repo.HumanOTPEmailRemovedType, - user_repo.HumanU2FTokenAddedType, - user_repo.HumanU2FTokenVerifiedType, - user_repo.HumanU2FTokenRemovedType, - user_repo.HumanPasswordlessTokenAddedType, - user_repo.HumanPasswordlessTokenVerifiedType, - user_repo.HumanPasswordlessTokenRemovedType, - user_repo.HumanMFAInitSkippedType, - user_repo.MachineChangedEventType, - user_repo.HumanPasswordChangedType, - user_repo.HumanInitialCodeAddedType, - user_repo.UserV1InitialCodeAddedType, - user_repo.UserV1InitializedCheckSucceededType, - user_repo.HumanInitializedCheckSucceededType, - user_repo.HumanPasswordlessInitCodeAddedType, - user_repo.HumanPasswordlessInitCodeRequestedType: - user, err = u.view.UserByID(event.AggregateID, event.InstanceID) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - logging.WithFields( - "instance", event.InstanceID, - "userID", event.AggregateID, - "eventType", event.Type, - ).Info("user not found in view") - query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0, user.EventTypes()) +//nolint:gocognit +func (u *User) ProcessUser(event eventstore.Event) (_ *handler.Statement, err error) { + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + user := new(view_model.UserView) + switch event.Type() { + case user_repo.UserV1AddedType, + user_repo.MachineAddedEventType, + user_repo.HumanAddedType, + user_repo.UserV1RegisteredType, + user_repo.HumanRegisteredType: + err = user.AppendEvent(event) if err != nil { return err } - events, err := u.es.FilterEvents(context.Background(), query) + err = u.fillLoginNames(user) + case user_repo.UserV1ProfileChangedType, + user_repo.UserV1EmailChangedType, + user_repo.UserV1EmailVerifiedType, + user_repo.UserV1PhoneChangedType, + user_repo.UserV1PhoneVerifiedType, + user_repo.UserV1PhoneRemovedType, + user_repo.UserV1AddressChangedType, + user_repo.UserDeactivatedType, + user_repo.UserReactivatedType, + user_repo.UserLockedType, + user_repo.UserUnlockedType, + user_repo.UserV1MFAOTPAddedType, + user_repo.UserV1MFAOTPVerifiedType, + user_repo.UserV1MFAOTPRemovedType, + user_repo.UserV1MFAInitSkippedType, + user_repo.UserV1PasswordChangedType, + user_repo.HumanProfileChangedType, + user_repo.HumanEmailChangedType, + user_repo.HumanEmailVerifiedType, + user_repo.HumanAvatarAddedType, + user_repo.HumanAvatarRemovedType, + user_repo.HumanPhoneChangedType, + user_repo.HumanPhoneVerifiedType, + user_repo.HumanPhoneRemovedType, + user_repo.HumanAddressChangedType, + user_repo.HumanMFAOTPAddedType, + user_repo.HumanMFAOTPVerifiedType, + user_repo.HumanMFAOTPRemovedType, + user_repo.HumanOTPSMSAddedType, + user_repo.HumanOTPSMSRemovedType, + user_repo.HumanOTPEmailAddedType, + user_repo.HumanOTPEmailRemovedType, + user_repo.HumanU2FTokenAddedType, + user_repo.HumanU2FTokenVerifiedType, + user_repo.HumanU2FTokenRemovedType, + user_repo.HumanPasswordlessTokenAddedType, + user_repo.HumanPasswordlessTokenVerifiedType, + user_repo.HumanPasswordlessTokenRemovedType, + user_repo.HumanMFAInitSkippedType, + user_repo.MachineChangedEventType, + user_repo.HumanPasswordChangedType, + user_repo.HumanInitialCodeAddedType, + user_repo.UserV1InitialCodeAddedType, + user_repo.UserV1InitializedCheckSucceededType, + user_repo.HumanInitializedCheckSucceededType, + user_repo.HumanPasswordlessInitCodeAddedType, + user_repo.HumanPasswordlessInitCodeRequestedType: + user, err = u.view.UserByID(event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil { - return err - } - user = &view_model.UserView{} - for _, e := range events { - if err = user.AppendEvent(e); err != nil { + if !errors.IsNotFound(err) { + return err + } + user, err = u.userFromEventstore(event.Aggregate(), user.EventTypes()) + if err != nil { return err } } - } - err = user.AppendEvent(event) - case user_repo.UserDomainClaimedType, - user_repo.UserUserNameChangedType: - user, err = u.view.UserByID(event.AggregateID, event.InstanceID) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - logging.WithFields( - "instance", event.InstanceID, - "userID", event.AggregateID, - "eventType", event.Type, - ).Info("user not found in view") - query, err := usr_view.UserByIDQuery(event.AggregateID, event.InstanceID, 0, user.EventTypes()) + err = user.AppendEvent(event) + case user_repo.UserDomainClaimedType, + user_repo.UserUserNameChangedType: + user, err = u.view.UserByID(event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil { - return err - } - events, err := u.es.FilterEvents(context.Background(), query) - if err != nil { - return err - } - user = &view_model.UserView{} - for _, e := range events { - if err = user.AppendEvent(e); err != nil { + if !errors.IsNotFound(err) { + return err + } + user, err = u.userFromEventstore(event.Aggregate(), user.EventTypes()) + if err != nil { return err } } + err = user.AppendEvent(event) + if err != nil { + return err + } + err = u.fillLoginNames(user) + case user_repo.UserRemovedType: + return u.view.DeleteUser(event.Aggregate().ID, event.Aggregate().InstanceID, event) + default: + return nil } - err = user.AppendEvent(event) if err != nil { return err } - err = u.fillLoginNames(user) - case user_repo.UserRemovedType: - return u.view.DeleteUser(event.AggregateID, event.InstanceID, event) - default: - return u.view.ProcessedUserSequence(event) - } - if err != nil { - return err - } - return u.view.PutUser(user, event) + return u.view.PutUser(user, event) + }), nil } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { @@ -236,38 +436,44 @@ func (u *User) fillLoginNames(user *view_model.UserView) (err error) { return nil } -func (u *User) ProcessOrg(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { - case org.OrgDomainVerifiedEventType, - org.OrgDomainRemovedEventType, - org.DomainPolicyAddedEventType, - org.DomainPolicyChangedEventType, - org.DomainPolicyRemovedEventType: - return u.fillLoginNamesOnOrgUsers(event) - case org.OrgDomainPrimarySetEventType: - return u.fillPreferredLoginNamesOnOrgUsers(event) - case org.OrgRemovedEventType: - return u.view.UpdateOrgOwnerRemovedUsers(event) - default: - return u.view.ProcessedUserSequence(event) - } +func (u *User) ProcessOrg(event eventstore.Event) (_ *handler.Statement, err error) { + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + switch event.Type() { + case org.OrgDomainVerifiedEventType, + org.OrgDomainRemovedEventType, + org.DomainPolicyAddedEventType, + org.DomainPolicyChangedEventType, + org.DomainPolicyRemovedEventType: + return u.fillLoginNamesOnOrgUsers(event) + case org.OrgDomainPrimarySetEventType: + return u.fillPreferredLoginNamesOnOrgUsers(event) + case org.OrgRemovedEventType: + return u.view.UpdateOrgOwnerRemovedUsers(event) + default: + return nil + } + }), nil } -func (u *User) ProcessInstance(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { +func (u *User) ProcessInstance(event eventstore.Event) (_ *handler.Statement, err error) { + switch event.Type() { case instance.InstanceRemovedEventType: - return u.view.DeleteInstanceUsers(event) + return handler.NewStatement(event, + func(ex handler.Executer, projectionName string) error { + return u.view.DeleteInstanceUsers(event) + }, + ), nil default: - return u.view.ProcessedUserSequence(event) + return handler.NewNoOpStatement(event), nil } } -func (u *User) fillLoginNamesOnOrgUsers(event *es_models.Event) error { - userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.ResourceOwner, event.InstanceID) +func (u *User) fillLoginNamesOnOrgUsers(event eventstore.Event) error { + userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.Aggregate().ResourceOwner, event.Aggregate().InstanceID) if err != nil { return err } - users, err := u.view.UsersByOrgID(event.AggregateID, event.InstanceID) + users, err := u.view.UsersByOrgID(event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil { return err } @@ -277,15 +483,15 @@ func (u *User) fillLoginNamesOnOrgUsers(event *es_models.Event) error { return u.view.PutUsers(users, event) } -func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { - userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.ResourceOwner, event.InstanceID) +func (u *User) fillPreferredLoginNamesOnOrgUsers(event eventstore.Event) error { + userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.Aggregate().ResourceOwner, event.Aggregate().InstanceID) if err != nil { return err } if !userLoginMustBeDomain { return nil } - users, err := u.view.UsersByOrgID(event.AggregateID, event.InstanceID) + users, err := u.view.UsersByOrgID(event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil { return err } @@ -295,17 +501,8 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { return u.view.PutUsers(users, event) } -func (u *User) OnError(event *es_models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in user handler") - return spooler.HandleError(event, err, u.view.GetLatestUserFailedEvent, u.view.ProcessedUserFailedEvent, u.view.ProcessedUserSequence, u.errorCountUntilSkip) -} - -func (u *User) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(u.view.UpdateUserSpoolerRunTimestamp, instanceIDs) -} - func (u *User) getOrgByID(ctx context.Context, orgID, instanceID string) (*org_model.Org, error) { - orgQuery, err := view.OrgByIDQuery(orgID, instanceID, 0) + query, err := org_view.OrgByIDQuery(orgID, instanceID, 0) if err != nil { return nil, err } @@ -315,8 +512,11 @@ func (u *User) getOrgByID(ctx context.Context, orgID, instanceID string) (*org_m AggregateID: orgID, }, } - err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, orgQuery) - if err != nil && !errors.IsNotFound(err) { + events, err := u.es.Filter(ctx, query) + if err != nil { + return nil, err + } + if err = esOrg.AppendEvents(events...); err != nil { return nil, err } if esOrg.Sequence == 0 { @@ -334,9 +534,27 @@ func (u *User) loginNameInformation(ctx context.Context, orgID string, instanceI if org.DomainPolicy != nil { return org.DomainPolicy.UserLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil } - policy, err := u.queries.DefaultDomainPolicy(withInstanceID(ctx, org.InstanceID)) + policy, err := u.queries.DefaultDomainPolicy(authz.WithInstanceID(ctx, org.InstanceID)) if err != nil { return false, "", nil, err } return policy.UserLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil } + +func (u *User) userFromEventstore(agg *eventstore.Aggregate, eventTypes []eventstore.EventType) (*view_model.UserView, error) { + query, err := usr_view.UserByIDQuery(agg.ID, agg.InstanceID, 0, eventTypes) + if err != nil { + return nil, err + } + events, err := u.es.Filter(context.Background(), query) + if err != nil { + return nil, err + } + user := &view_model.UserView{} + for _, e := range events { + if err = user.AppendEvent(e); err != nil { + return nil, err + } + } + return user, nil +} diff --git a/internal/auth/repository/eventsourcing/handler/user_session.go b/internal/auth/repository/eventsourcing/handler/user_session.go index a0148edd6..5c8807cc2 100644 --- a/internal/auth/repository/eventsourcing/handler/user_session.go +++ b/internal/auth/repository/eventsourcing/handler/user_session.go @@ -3,19 +3,16 @@ package handler import ( "context" - "github.com/zitadel/logging" - + "github.com/zitadel/zitadel/internal/api/authz" + auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - es_sdk "github.com/zitadel/zitadel/internal/eventstore/v1/sdk" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" org_model "github.com/zitadel/zitadel/internal/org/model" org_es_model "github.com/zitadel/zitadel/internal/org/repository/eventsourcing/model" - "github.com/zitadel/zitadel/internal/org/repository/view" + org_view "github.com/zitadel/zitadel/internal/org/repository/view" query2 "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -28,158 +25,299 @@ const ( ) type UserSession struct { - handler - subscription *v1.Subscription - queries *query2.Queries + queries *query2.Queries + view *auth_view.View + es handler.EventStore } -func newUserSession(ctx context.Context, handler handler, queries *query2.Queries) *UserSession { - h := &UserSession{ - handler: handler, - queries: queries, - } +var _ handler.Projection = (*UserSession)(nil) - h.subscribe(ctx) - - return h +func newUserSession( + ctx context.Context, + config handler.Config, + view *auth_view.View, + queries *query2.Queries, +) *handler.Handler { + return handler.NewHandler( + ctx, + &config, + &UserSession{ + queries: queries, + view: view, + es: config.Eventstore, + }, + ) } -func (u *UserSession) subscribe(ctx context.Context) { - u.subscription = u.es.Subscribe(u.AggregateTypes()...) - go func() { - for event := range u.subscription.Events { - query.ReduceEvent(ctx, u, event) - } - }() -} - -func (u *UserSession) ViewModel() string { +// Name implements [handler.Projection] +func (*UserSession) Name() string { return userSessionTable } -func (u *UserSession) Subscription() *v1.Subscription { - return u.subscription -} - -func (_ *UserSession) AggregateTypes() []models.AggregateType { - return []models.AggregateType{user.AggregateType, org.AggregateType, instance.AggregateType} -} - -func (u *UserSession) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - sequence, err := u.view.GetLatestUserSessionSequence(ctx, instanceID) - if err != nil { - return 0, err +// Reducers implements [handler.Projection] +func (s *UserSession) Reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: user.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: user.UserV1PasswordCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1PasswordCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1MFAOTPCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1MFAOTPCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1SignedOutType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.UserIDPLoginCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.HumanMFAOTPCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.HumanMFAOTPCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanU2FTokenCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.HumanU2FTokenCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordlessTokenCheckSucceededType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordlessTokenCheckFailedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanSignedOutType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1PasswordChangedType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1MFAOTPRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.UserV1ProfileChangedType, + Reduce: s.Reduce, + }, + { + Event: user.UserLockedType, + Reduce: s.Reduce, + }, + { + Event: user.UserDeactivatedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordChangedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanMFAOTPRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanProfileChangedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanAvatarAddedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanAvatarRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.UserDomainClaimedType, + Reduce: s.Reduce, + }, + { + Event: user.UserUserNameChangedType, + Reduce: s.Reduce, + }, + { + Event: user.UserIDPLinkRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.UserIDPLinkCascadeRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanPasswordlessTokenRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.HumanU2FTokenRemovedType, + Reduce: s.Reduce, + }, + { + Event: user.UserRemovedType, + Reduce: s.Reduce, + }, + }, + }, + { + Aggregate: org.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: org.OrgDomainPrimarySetEventType, + Reduce: s.Reduce, + }, + { + Event: org.OrgRemovedEventType, + Reduce: s.Reduce, + }, + }, + }, + { + Aggregate: instance.AggregateType, + EventReducers: []handler.EventReducer{ + { + Event: instance.InstanceRemovedEventType, + Reduce: s.Reduce, + }, + }, + }, } - return sequence.CurrentSequence, nil } -func (u *UserSession) EventQuery(ctx context.Context, instanceIDs []string) (*models.SearchQuery, error) { - sequences, err := u.view.GetLatestUserSessionSequences(ctx, instanceIDs) - if err != nil { - return nil, err - } - return newSearchQuery(sequences, u.AggregateTypes(), instanceIDs), nil +func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err error) { + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + var session *view_model.UserSessionView + switch event.Type() { + case user.UserV1PasswordCheckSucceededType, + user.UserV1PasswordCheckFailedType, + user.UserV1MFAOTPCheckSucceededType, + user.UserV1MFAOTPCheckFailedType, + user.UserV1SignedOutType, + user.HumanPasswordCheckSucceededType, + user.HumanPasswordCheckFailedType, + user.UserIDPLoginCheckSucceededType, + user.HumanMFAOTPCheckSucceededType, + user.HumanMFAOTPCheckFailedType, + user.HumanU2FTokenCheckSucceededType, + user.HumanU2FTokenCheckFailedType, + user.HumanPasswordlessTokenCheckSucceededType, + user.HumanPasswordlessTokenCheckFailedType, + user.HumanSignedOutType: + + eventData, err := view_model.UserSessionFromEvent(event) + if err != nil { + return err + } + session, err = u.view.UserSessionByIDs(eventData.UserAgentID, event.Aggregate().ID, event.Aggregate().InstanceID) + if err != nil { + if !errors.IsNotFound(err) { + return err + } + session = &view_model.UserSessionView{ + CreationDate: event.CreatedAt(), + ResourceOwner: event.Aggregate().ResourceOwner, + UserAgentID: eventData.UserAgentID, + UserID: event.Aggregate().ID, + State: int32(domain.UserSessionStateActive), + InstanceID: event.Aggregate().InstanceID, + } + } + return u.updateSession(session, event) + case user.UserV1PasswordChangedType, + user.UserV1MFAOTPRemovedType, + user.UserV1ProfileChangedType, + user.UserLockedType, + user.UserDeactivatedType, + user.HumanPasswordChangedType, + user.HumanMFAOTPRemovedType, + user.HumanProfileChangedType, + user.HumanAvatarAddedType, + user.HumanAvatarRemovedType, + user.UserDomainClaimedType, + user.UserUserNameChangedType, + user.UserIDPLinkRemovedType, + user.UserIDPLinkCascadeRemovedType, + user.HumanPasswordlessTokenRemovedType, + user.HumanU2FTokenRemovedType: + sessions, err := u.view.UserSessionsByUserID(event.Aggregate().ID, event.Aggregate().InstanceID) + if err != nil || len(sessions) == 0 { + return err + } + if err = u.appendEventOnSessions(sessions, event); err != nil { + return err + } + if err = u.view.PutUserSessions(sessions); err != nil { + return err + } + return nil + case org.OrgDomainPrimarySetEventType: + return u.fillLoginNamesOnOrgUsers(event) + case user.UserRemovedType: + return u.view.DeleteUserSessions(event.Aggregate().ID, event.Aggregate().InstanceID) + case instance.InstanceRemovedEventType: + return u.view.DeleteInstanceUserSessions(event.Aggregate().InstanceID) + case org.OrgRemovedEventType: + return u.view.DeleteOrgUserSessions(event) + default: + return nil + } + }), nil } -func (u *UserSession) Reduce(event *models.Event) (err error) { - var session *view_model.UserSessionView - switch eventstore.EventType(event.Type) { - case user.UserV1PasswordCheckSucceededType, - user.UserV1PasswordCheckFailedType, - user.UserV1MFAOTPCheckSucceededType, - user.UserV1MFAOTPCheckFailedType, - user.UserV1SignedOutType, - user.HumanPasswordCheckSucceededType, - user.HumanPasswordCheckFailedType, - user.UserIDPLoginCheckSucceededType, - user.HumanMFAOTPCheckSucceededType, - user.HumanMFAOTPCheckFailedType, - user.HumanU2FTokenCheckSucceededType, - user.HumanU2FTokenCheckFailedType, - user.HumanPasswordlessTokenCheckSucceededType, - user.HumanPasswordlessTokenCheckFailedType, - user.HumanSignedOutType: - eventData, err := view_model.UserSessionFromEvent(event) - if err != nil { +func (u *UserSession) appendEventOnSessions(sessions []*view_model.UserSessionView, event eventstore.Event) error { + for _, session := range sessions { + if err := session.AppendEvent(event); err != nil { return err } - session, err = u.view.UserSessionByIDs(eventData.UserAgentID, event.AggregateID, event.InstanceID) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - session = &view_model.UserSessionView{ - CreationDate: event.CreationDate, - ResourceOwner: event.ResourceOwner, - UserAgentID: eventData.UserAgentID, - UserID: event.AggregateID, - State: int32(domain.UserSessionStateActive), - InstanceID: event.InstanceID, - } - } - return u.updateSession(session, event) - case user.UserV1PasswordChangedType, - user.UserV1MFAOTPRemovedType, - user.UserV1ProfileChangedType, - user.UserLockedType, - user.UserDeactivatedType, - user.HumanPasswordChangedType, - user.HumanMFAOTPRemovedType, - user.HumanProfileChangedType, - user.HumanAvatarAddedType, - user.HumanAvatarRemovedType, - user.UserDomainClaimedType, - user.UserUserNameChangedType, - user.UserIDPLinkRemovedType, - user.UserIDPLinkCascadeRemovedType, - user.HumanPasswordlessTokenRemovedType, - user.HumanU2FTokenRemovedType: - sessions, err := u.view.UserSessionsByUserID(event.AggregateID, event.InstanceID) - if err != nil { + if err := u.fillUserInfo(session); err != nil { return err } - if len(sessions) == 0 { - return u.view.ProcessedUserSessionSequence(event) - } - for _, session := range sessions { - if err := session.AppendEvent(event); err != nil { - return err - } - if err := u.fillUserInfo(session); err != nil { - return err - } - } - return u.view.PutUserSessions(sessions, event) - case org.OrgDomainPrimarySetEventType: - return u.fillLoginNamesOnOrgUsers(event) - case user.UserRemovedType: - return u.view.DeleteUserSessions(event.AggregateID, event.InstanceID, event) - case instance.InstanceRemovedEventType: - return u.view.DeleteInstanceUserSessions(event) - case org.OrgRemovedEventType: - return u.view.DeleteOrgUserSessions(event) - default: - return u.view.ProcessedUserSessionSequence(event) } + return nil } -func (u *UserSession) OnError(event *models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in user session handler") - return spooler.HandleError(event, err, u.view.GetLatestUserSessionFailedEvent, u.view.ProcessedUserSessionFailedEvent, u.view.ProcessedUserSessionSequence, u.errorCountUntilSkip) -} - -func (u *UserSession) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(u.view.UpdateUserSessionSpoolerRunTimestamp, instanceIDs) -} - -func (u *UserSession) updateSession(session *view_model.UserSessionView, event *models.Event) error { +func (u *UserSession) updateSession(session *view_model.UserSessionView, event eventstore.Event) error { if err := session.AppendEvent(event); err != nil { return err } if err := u.fillUserInfo(session); err != nil { return err } - return u.view.PutUserSession(session, event) + if err := u.view.PutUserSession(session); err != nil { + return err + } + return nil } func (u *UserSession) fillUserInfo(session *view_model.UserSessionView) error { @@ -194,15 +332,15 @@ func (u *UserSession) fillUserInfo(session *view_model.UserSessionView) error { return nil } -func (u *UserSession) fillLoginNamesOnOrgUsers(event *models.Event) error { - sessions, err := u.view.UserSessionsByOrgID(event.ResourceOwner, event.InstanceID) +func (u *UserSession) fillLoginNamesOnOrgUsers(event eventstore.Event) error { + sessions, err := u.view.UserSessionsByOrgID(event.Aggregate().ResourceOwner, event.Aggregate().InstanceID) if err != nil { return err } if len(sessions) == 0 { - return u.view.ProcessedUserSessionSequence(event) + return nil } - userLoginMustBeDomain, primaryDomain, err := u.loginNameInformation(context.Background(), event.ResourceOwner, event.InstanceID) + userLoginMustBeDomain, primaryDomain, err := u.loginNameInformation(context.Background(), event.Aggregate().ResourceOwner, event.Aggregate().InstanceID) if err != nil { return err } @@ -212,7 +350,7 @@ func (u *UserSession) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, session := range sessions { session.LoginName = session.UserName + "@" + primaryDomain } - return u.view.PutUserSessions(sessions, event) + return u.view.PutUserSessions(sessions) } func (u *UserSession) loginNameInformation(ctx context.Context, orgID string, instanceID string) (userLoginMustBeDomain bool, primaryDomain string, err error) { @@ -223,7 +361,7 @@ func (u *UserSession) loginNameInformation(ctx context.Context, orgID string, in if org.DomainPolicy != nil { return org.DomainPolicy.UserLoginMustBeDomain, org.GetPrimaryDomain().Domain, nil } - policy, err := u.queries.DefaultDomainPolicy(withInstanceID(ctx, org.InstanceID)) + policy, err := u.queries.DefaultDomainPolicy(authz.WithInstanceID(ctx, org.InstanceID)) if err != nil { return false, "", err } @@ -231,7 +369,7 @@ func (u *UserSession) loginNameInformation(ctx context.Context, orgID string, in } func (u *UserSession) getOrgByID(ctx context.Context, orgID, instanceID string) (*org_model.Org, error) { - orgQuery, err := view.OrgByIDQuery(orgID, instanceID, 0) + query, err := org_view.OrgByIDQuery(orgID, instanceID, 0) if err != nil { return nil, err } @@ -241,13 +379,16 @@ func (u *UserSession) getOrgByID(ctx context.Context, orgID, instanceID string) AggregateID: orgID, }, } - err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, orgQuery) - if err != nil && !errors.IsNotFound(err) { + events, err := u.es.Filter(ctx, query) + if err != nil { return nil, err } + if err = esOrg.AppendEvents(events...); err != nil { + return nil, err + } + if esOrg.Sequence == 0 { return nil, errors.ThrowNotFound(nil, "EVENT-3m9vs", "Errors.Org.NotFound") } - return org_es_model.OrgToModel(esOrg), nil } diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index b8d41e009..f3d11e74d 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -5,7 +5,7 @@ import ( "github.com/zitadel/zitadel/feature" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/eventstore" - "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/spooler" + auth_handler "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/handler" auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/auth_request/repository/cache" "github.com/zitadel/zitadel/internal/command" @@ -13,20 +13,16 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" eventstore2 "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - es_spol "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/query" ) type Config struct { SearchLimit uint64 - Spooler spooler.SpoolerConfig + Spooler auth_handler.Config } type EsRepository struct { - spooler *es_spol.Spooler - Eventstore v1.Eventstore eventstore.UserRepo eventstore.AuthRequestRepo eventstore.TokenRepo @@ -35,25 +31,19 @@ type EsRepository struct { eventstore.OrgRepository } -func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *database.DB, esV2 *eventstore2.Eventstore, oidcEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm, allowOrderByCreationDate bool) (*EsRepository, error) { - es, err := v1.Start(dbClient, allowOrderByCreationDate) +func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *database.DB, esV2 *eventstore2.Eventstore, oidcEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm) (*EsRepository, error) { + view, err := auth_view.StartView(dbClient, oidcEncryption, queries, esV2) if err != nil { return nil, err } - idGenerator := id.SonyFlakeGenerator() - view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator, es) - if err != nil { - return nil, err - } + auth_handler.Register(ctx, conf.Spooler, view, queries) authReq := cache.Start(dbClient) - spool := spooler.StartSpooler(ctx, conf.Spooler, es, esV2, view, dbClient, queries) - userRepo := eventstore.UserRepo{ SearchLimit: conf.SearchLimit, - Eventstore: es, + Eventstore: esV2, View: view, Query: queries, SystemDefaults: systemDefaults, @@ -64,8 +54,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c view, } return &EsRepository{ - spool, - es, userRepo, eventstore.AuthRequestRepo{ PrivacyPolicyProvider: queries, @@ -75,7 +63,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c OrgViewProvider: queries, AuthRequests: authReq, View: view, - Eventstore: es, UserCodeAlg: userEncryption, UserSessionViewProvider: view, UserViewProvider: view, @@ -90,15 +77,15 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c ApplicationProvider: queries, CustomTextProvider: queries, FeatureCheck: feature.NewCheck(esV2), - IdGenerator: idGenerator, + IdGenerator: id.SonyFlakeGenerator(), }, eventstore.TokenRepo{ View: view, - Eventstore: es, + Eventstore: esV2, }, eventstore.RefreshTokenRepo{ View: view, - Eventstore: es, + Eventstore: esV2, SearchLimit: conf.SearchLimit, KeyAlgorithm: oidcEncryption, }, @@ -109,7 +96,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c SearchLimit: conf.SearchLimit, View: view, SystemDefaults: systemDefaults, - Eventstore: es, + Eventstore: esV2, Query: queries, }, }, nil diff --git a/internal/auth/repository/eventsourcing/spooler/lock.go b/internal/auth/repository/eventsourcing/spooler/lock.go deleted file mode 100644 index 1861eb446..000000000 --- a/internal/auth/repository/eventsourcing/spooler/lock.go +++ /dev/null @@ -1,24 +0,0 @@ -package spooler - -import ( - "database/sql" - "time" - - es_locker "github.com/zitadel/zitadel/internal/eventstore/v1/locker" -) - -const ( - lockTable = "auth.locks" -) - -type locker struct { - dbClient *sql.DB -} - -func NewLocker(client *sql.DB) *locker { - return &locker{dbClient: client} -} - -func (l *locker) Renew(lockerID, viewModel, instanceID string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, instanceID, waitTime) -} diff --git a/internal/auth/repository/eventsourcing/spooler/spooler.go b/internal/auth/repository/eventsourcing/spooler/spooler.go deleted file mode 100644 index dfbfc73b2..000000000 --- a/internal/auth/repository/eventsourcing/spooler/spooler.go +++ /dev/null @@ -1,35 +0,0 @@ -package spooler - -import ( - "context" - - "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/handler" - "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" - "github.com/zitadel/zitadel/internal/query" -) - -type SpoolerConfig struct { - BulkLimit uint64 - FailureCountUntilSkip uint64 - ConcurrentWorkers int - ConcurrentInstances int - Handlers handler.Configs -} - -func StartSpooler(ctx context.Context, c SpoolerConfig, es v1.Eventstore, esV2 *eventstore.Eventstore, view *view.View, client *database.DB, queries *query.Queries) *spooler.Spooler { - spoolerConfig := spooler.Config{ - Eventstore: es, - EventstoreV2: esV2, - Locker: &locker{dbClient: client.DB}, - ConcurrentWorkers: c.ConcurrentWorkers, - ConcurrentInstances: c.ConcurrentInstances, - ViewHandlers: handler.Register(ctx, c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, queries), - } - spool := spoolerConfig.New() - spool.Start() - return spool -} diff --git a/internal/auth/repository/eventsourcing/view/error_event.go b/internal/auth/repository/eventsourcing/view/error_event.go deleted file mode 100644 index d343d5820..000000000 --- a/internal/auth/repository/eventsourcing/view/error_event.go +++ /dev/null @@ -1,17 +0,0 @@ -package view - -import ( - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - errTable = "auth.failed_events" -) - -func (v *View) saveFailedEvent(failedEvent *repository.FailedEvent) error { - return repository.SaveFailedEvent(v.Db, errTable, failedEvent) -} - -func (v *View) latestFailedEvent(viewName, instanceID string, sequence uint64) (*repository.FailedEvent, error) { - return repository.LatestFailedEvent(v.Db, errTable, viewName, instanceID, sequence) -} diff --git a/internal/auth/repository/eventsourcing/view/refresh_token.go b/internal/auth/repository/eventsourcing/view/refresh_token.go index 2740c2ec8..64933bc7c 100644 --- a/internal/auth/repository/eventsourcing/view/refresh_token.go +++ b/internal/auth/repository/eventsourcing/view/refresh_token.go @@ -3,12 +3,14 @@ package view import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/query" user_model "github.com/zitadel/zitadel/internal/user/model" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -27,36 +29,28 @@ func (v *View) SearchRefreshTokens(request *user_model.RefreshTokenSearchRequest return usr_view.SearchRefreshTokens(v.Db, refreshTokenTable, request) } -func (v *View) PutRefreshToken(token *model.RefreshTokenView, event *models.Event) error { - err := usr_view.PutRefreshToken(v.Db, refreshTokenTable, token) - if err != nil { - return err - } - return v.ProcessedRefreshTokenSequence(event) +func (v *View) PutRefreshToken(token *model.RefreshTokenView) error { + return usr_view.PutRefreshToken(v.Db, refreshTokenTable, token) } -func (v *View) PutRefreshTokens(token []*model.RefreshTokenView, event *models.Event) error { - err := usr_view.PutRefreshTokens(v.Db, refreshTokenTable, token...) - if err != nil { - return err - } - return v.ProcessedRefreshTokenSequence(event) +func (v *View) PutRefreshTokens(token []*model.RefreshTokenView) error { + return usr_view.PutRefreshTokens(v.Db, refreshTokenTable, token...) } -func (v *View) DeleteRefreshToken(tokenID, instanceID string, event *models.Event) error { +func (v *View) DeleteRefreshToken(tokenID, instanceID string) error { err := usr_view.DeleteRefreshToken(v.Db, refreshTokenTable, tokenID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedRefreshTokenSequence(event) + return nil } -func (v *View) DeleteUserRefreshTokens(userID, instanceID string, event *models.Event) error { +func (v *View) DeleteUserRefreshTokens(userID, instanceID string) error { err := usr_view.DeleteUserRefreshTokens(v.Db, refreshTokenTable, userID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedRefreshTokenSequence(event) + return nil } func (v *View) DeleteApplicationRefreshTokens(event *models.Event, ids ...string) error { @@ -64,45 +58,40 @@ func (v *View) DeleteApplicationRefreshTokens(event *models.Event, ids ...string if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedRefreshTokenSequence(event) + return nil } -func (v *View) DeleteInstanceRefreshTokens(event *models.Event) error { - err := usr_view.DeleteInstanceRefreshTokens(v.Db, refreshTokenTable, event.InstanceID) +func (v *View) DeleteInstanceRefreshTokens(instanceID string) error { + err := usr_view.DeleteInstanceRefreshTokens(v.Db, refreshTokenTable, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedRefreshTokenSequence(event) + return nil } -func (v *View) DeleteOrgRefreshTokens(event *models.Event) error { - err := usr_view.DeleteOrgRefreshTokens(v.Db, refreshTokenTable, event.InstanceID, event.ResourceOwner) +func (v *View) DeleteOrgRefreshTokens(event eventstore.Event) error { + err := usr_view.DeleteOrgRefreshTokens(v.Db, refreshTokenTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedRefreshTokenSequence(event) + return nil } -func (v *View) GetLatestRefreshTokenSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(ctx, refreshTokenTable, instanceID) -} - -func (v *View) GetLatestRefreshTokenSequences(ctx context.Context, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return v.latestSequences(ctx, refreshTokenTable, instanceIDs) -} - -func (v *View) ProcessedRefreshTokenSequence(event *models.Event) error { - return v.saveCurrentSequence(refreshTokenTable, event) -} - -func (v *View) UpdateRefreshTokenSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(refreshTokenTable, instanceIDs) -} - -func (v *View) GetLatestRefreshTokenFailedEvent(sequence uint64, instanceID string) (*repository.FailedEvent, error) { - return v.latestFailedEvent(refreshTokenTable, instanceID, sequence) -} - -func (v *View) ProcessedRefreshTokenFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) +func (v *View) GetLatestRefreshTokenSequence(ctx context.Context) (_ *query.CurrentState, err error) { + q := &query.CurrentStateSearchQueries{ + Queries: make([]query.SearchQuery, 2), + } + q.Queries[0], err = query.NewCurrentStatesInstanceIDSearchQuery(authz.GetInstance(ctx).InstanceID()) + if err != nil { + return nil, err + } + q.Queries[1], err = query.NewCurrentStatesProjectionSearchQuery(refreshTokenTable) + if err != nil { + return nil, err + } + states, err := v.query.SearchCurrentStates(ctx, q) + if err != nil || states.SearchResponse.Count == 0 { + return nil, err + } + return states.CurrentStates[0], nil } diff --git a/internal/auth/repository/eventsourcing/view/sequence.go b/internal/auth/repository/eventsourcing/view/sequence.go deleted file mode 100644 index 71c77cb6a..000000000 --- a/internal/auth/repository/eventsourcing/view/sequence.go +++ /dev/null @@ -1,39 +0,0 @@ -package view - -import ( - "context" - "time" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - sequencesTable = "auth.current_sequences" -) - -func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, event.InstanceID, event.Sequence, event.CreationDate) -} - -func (v *View) latestSequence(ctx context.Context, viewName, instanceID string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, v.TimeTravel(ctx, sequencesTable), viewName, instanceID) -} - -func (v *View) latestSequences(ctx context.Context, viewName string, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return repository.LatestSequences(v.Db, v.TimeTravel(ctx, sequencesTable), viewName, instanceIDs) -} - -func (v *View) updateSpoolerRunSequence(viewName string, instanceIDs []string) error { - currentSequences, err := repository.LatestSequences(v.Db, sequencesTable, viewName, instanceIDs) - if err != nil { - return err - } - for _, currentSequence := range currentSequences { - if currentSequence.ViewName == "" { - currentSequence.ViewName = viewName - } - currentSequence.LastSuccessfulSpoolerRun = time.Now() - } - return repository.UpdateCurrentSequences(v.Db, sequencesTable, currentSequences) -} diff --git a/internal/auth/repository/eventsourcing/view/token.go b/internal/auth/repository/eventsourcing/view/token.go index 3f3909cdb..549907d89 100644 --- a/internal/auth/repository/eventsourcing/view/token.go +++ b/internal/auth/repository/eventsourcing/view/token.go @@ -4,10 +4,10 @@ import ( "context" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/query" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -22,98 +22,85 @@ func (v *View) TokensByUserID(userID, instanceID string) ([]*model.TokenView, er return usr_view.TokensByUserID(v.Db, tokenTable, userID, instanceID) } -func (v *View) PutToken(token *model.TokenView, event *models.Event) error { - err := usr_view.PutToken(v.Db, tokenTable, token) - if err != nil { - return err - } - return v.ProcessedTokenSequence(event) +func (v *View) PutToken(token *model.TokenView) error { + return usr_view.PutToken(v.Db, tokenTable, token) } -func (v *View) PutTokens(token []*model.TokenView, event *models.Event) error { - err := usr_view.PutTokens(v.Db, tokenTable, token...) - if err != nil { - return err - } - return v.ProcessedTokenSequence(event) +func (v *View) PutTokens(token []*model.TokenView) error { + return usr_view.PutTokens(v.Db, tokenTable, token...) } -func (v *View) DeleteToken(tokenID, instanceID string, event *models.Event) error { +func (v *View) DeleteToken(tokenID, instanceID string) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteSessionTokens(agentID, userID, instanceID string, event *models.Event) error { - err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID, instanceID) +func (v *View) DeleteSessionTokens(agentID string, event eventstore.Event) error { + err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteUserTokens(userID, instanceID string, event *models.Event) error { - err := usr_view.DeleteUserTokens(v.Db, tokenTable, userID, instanceID) +func (v *View) DeleteUserTokens(event eventstore.Event) error { + err := usr_view.DeleteUserTokens(v.Db, tokenTable, event.Aggregate().ID, event.Aggregate().InstanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteApplicationTokens(event *models.Event, ids ...string) error { - err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, event.InstanceID, ids) +func (v *View) DeleteApplicationTokens(event eventstore.Event, ids ...string) error { + err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, event.Aggregate().InstanceID, ids) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteTokensFromRefreshToken(refreshTokenID, instanceID string, event *models.Event) error { +func (v *View) DeleteTokensFromRefreshToken(refreshTokenID, instanceID string) error { err := usr_view.DeleteTokensFromRefreshToken(v.Db, tokenTable, refreshTokenID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteInstanceTokens(event *models.Event) error { - err := usr_view.DeleteInstanceTokens(v.Db, tokenTable, event.InstanceID) +func (v *View) DeleteInstanceTokens(event eventstore.Event) error { + err := usr_view.DeleteInstanceTokens(v.Db, tokenTable, event.Aggregate().InstanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) DeleteOrgTokens(event *models.Event) error { - err := usr_view.DeleteOrgTokens(v.Db, tokenTable, event.InstanceID, event.ResourceOwner) +func (v *View) DeleteOrgTokens(event eventstore.Event) error { + err := usr_view.DeleteOrgTokens(v.Db, tokenTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) GetLatestTokenSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(ctx, tokenTable, instanceID) -} - -func (v *View) GetLatestTokenSequences(ctx context.Context, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return v.latestSequences(ctx, tokenTable, instanceIDs) -} - -func (v *View) ProcessedTokenSequence(event *models.Event) error { - return v.saveCurrentSequence(tokenTable, event) -} - -func (v *View) UpdateTokenSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(tokenTable, instanceIDs) -} - -func (v *View) GetLatestTokenFailedEvent(sequence uint64, instanceID string) (*repository.FailedEvent, error) { - return v.latestFailedEvent(tokenTable, instanceID, sequence) -} - -func (v *View) ProcessedTokenFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) +func (v *View) GetLatestTokenSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) { + q := &query.CurrentStateSearchQueries{ + Queries: make([]query.SearchQuery, 2), + } + q.Queries[0], err = query.NewCurrentStatesInstanceIDSearchQuery(instanceID) + if err != nil { + return nil, err + } + q.Queries[1], err = query.NewCurrentStatesProjectionSearchQuery(tokenTable) + if err != nil { + return nil, err + } + states, err := v.query.SearchCurrentStates(ctx, q) + if err != nil || states.SearchResponse.Count == 0 { + return nil, err + } + return states.CurrentStates[0], nil } diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index b2008244a..2eaf53827 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -4,14 +4,12 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" usr_model "github.com/zitadel/zitadel/internal/user/model" "github.com/zitadel/zitadel/internal/user/repository/view" "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -104,7 +102,7 @@ func (v *View) userByID(ctx context.Context, instanceID string, queries ...query Errorf("could not get current sequence for userByID") user = new(model.UserView) if sequence != nil { - user.Sequence = sequence.CurrentSequence + user.Sequence = sequence.Sequence } } @@ -112,7 +110,7 @@ func (v *View) userByID(ctx context.Context, instanceID string, queries ...query if err != nil { return nil, err } - events, err := v.es.FilterEvents(ctx, query) + events, err := v.es.Filter(ctx, query) if err != nil && user.Sequence == 0 { return nil, err } else if err != nil { @@ -138,82 +136,53 @@ func (v *View) UsersByOrgID(orgID, instanceID string) ([]*model.UserView, error) return view.UsersByOrgID(v.Db, userTable, orgID, instanceID) } -func (v *View) UserIDsByDomain(domain, instanceID string) ([]string, error) { - return view.UserIDsByDomain(v.Db, userTable, domain, instanceID) +func (v *View) PutUser(user *model.UserView, event eventstore.Event) error { + return view.PutUser(v.Db, userTable, user) } -func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) { - return view.SearchUsers(v.Db, userTable, request) +func (v *View) PutUsers(users []*model.UserView, event eventstore.Event) error { + return view.PutUsers(v.Db, userTable, users...) } -func (v *View) GetGlobalUserByLoginName(email, instanceID string) (*model.UserView, error) { - return view.GetGlobalUserByLoginName(v.Db, userTable, email, instanceID) -} - -func (v *View) UserMFAs(userID, instanceID string) ([]*usr_model.MultiFactor, error) { - return view.UserMFAs(v.Db, userTable, userID, instanceID) -} - -func (v *View) PutUser(user *model.UserView, event *models.Event) error { - err := view.PutUser(v.Db, userTable, user) - if err != nil { - return err - } - return v.ProcessedUserSequence(event) -} - -func (v *View) PutUsers(users []*model.UserView, event *models.Event) error { - err := view.PutUsers(v.Db, userTable, users...) - if err != nil { - return err - } - return v.ProcessedUserSequence(event) -} - -func (v *View) DeleteUser(userID, instanceID string, event *models.Event) error { +func (v *View) DeleteUser(userID, instanceID string, event eventstore.Event) error { err := view.DeleteUser(v.Db, userTable, userID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSequence(event) + return nil } -func (v *View) DeleteInstanceUsers(event *models.Event) error { - err := view.DeleteInstanceUsers(v.Db, userTable, event.InstanceID) +func (v *View) DeleteInstanceUsers(event eventstore.Event) error { + err := view.DeleteInstanceUsers(v.Db, userTable, event.Aggregate().InstanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSequence(event) + return nil } -func (v *View) UpdateOrgOwnerRemovedUsers(event *models.Event) error { - err := view.UpdateOrgOwnerRemovedUsers(v.Db, userTable, event.InstanceID, event.AggregateID) +func (v *View) UpdateOrgOwnerRemovedUsers(event eventstore.Event) error { + err := view.UpdateOrgOwnerRemovedUsers(v.Db, userTable, event.Aggregate().InstanceID, event.Aggregate().ID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSequence(event) + return nil } -func (v *View) GetLatestUserSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(ctx, userTable, instanceID) -} - -func (v *View) GetLatestUserSequences(ctx context.Context, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return v.latestSequences(ctx, userTable, instanceIDs) -} - -func (v *View) ProcessedUserSequence(event *models.Event) error { - return v.saveCurrentSequence(userTable, event) -} - -func (v *View) UpdateUserSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(userTable, instanceIDs) -} - -func (v *View) GetLatestUserFailedEvent(sequence uint64, instanceID string) (*repository.FailedEvent, error) { - return v.latestFailedEvent(userTable, instanceID, sequence) -} - -func (v *View) ProcessedUserFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) +func (v *View) GetLatestUserSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) { + q := &query.CurrentStateSearchQueries{ + Queries: make([]query.SearchQuery, 2), + } + q.Queries[0], err = query.NewCurrentStatesInstanceIDSearchQuery(instanceID) + if err != nil { + return nil, err + } + q.Queries[1], err = query.NewCurrentStatesProjectionSearchQuery(userTable) + if err != nil { + return nil, err + } + states, err := v.query.SearchCurrentStates(ctx, q) + if err != nil || states.SearchResponse.Count == 0 { + return nil, err + } + return states.CurrentStates[0], nil } diff --git a/internal/auth/repository/eventsourcing/view/user_session.go b/internal/auth/repository/eventsourcing/view/user_session.go index 4e3803e77..5303b0dea 100644 --- a/internal/auth/repository/eventsourcing/view/user_session.go +++ b/internal/auth/repository/eventsourcing/view/user_session.go @@ -4,10 +4,10 @@ import ( "context" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/user/repository/view" "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -34,66 +34,53 @@ func (v *View) ActiveUserSessionsCount() (uint64, error) { return view.ActiveUserSessions(v.Db, userSessionTable) } -func (v *View) PutUserSession(userSession *model.UserSessionView, event *models.Event) error { - err := view.PutUserSession(v.Db, userSessionTable, userSession) - if err != nil { - return err - } - return v.ProcessedUserSessionSequence(event) +func (v *View) PutUserSession(userSession *model.UserSessionView) error { + return view.PutUserSession(v.Db, userSessionTable, userSession) } -func (v *View) PutUserSessions(userSession []*model.UserSessionView, event *models.Event) error { - err := view.PutUserSessions(v.Db, userSessionTable, userSession...) - if err != nil { - return err - } - return v.ProcessedUserSessionSequence(event) +func (v *View) PutUserSessions(userSession []*model.UserSessionView) error { + return view.PutUserSessions(v.Db, userSessionTable, userSession...) } -func (v *View) DeleteUserSessions(userID, instanceID string, event *models.Event) error { +func (v *View) DeleteUserSessions(userID, instanceID string) error { err := view.DeleteUserSessions(v.Db, userSessionTable, userID, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSessionSequence(event) + return nil } -func (v *View) DeleteInstanceUserSessions(event *models.Event) error { - err := view.DeleteInstanceUserSessions(v.Db, userSessionTable, event.InstanceID) +func (v *View) DeleteInstanceUserSessions(instanceID string) error { + err := view.DeleteInstanceUserSessions(v.Db, userSessionTable, instanceID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSessionSequence(event) + return nil } -func (v *View) DeleteOrgUserSessions(event *models.Event) error { - err := view.DeleteOrgUserSessions(v.Db, userSessionTable, event.InstanceID, event.ResourceOwner) +func (v *View) DeleteOrgUserSessions(event eventstore.Event) error { + err := view.DeleteOrgUserSessions(v.Db, userSessionTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedUserSessionSequence(event) + return nil } -func (v *View) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(ctx, userSessionTable, instanceID) -} - -func (v *View) GetLatestUserSessionSequences(ctx context.Context, instanceIDs []string) ([]*repository.CurrentSequence, error) { - return v.latestSequences(ctx, userSessionTable, instanceIDs) -} - -func (v *View) ProcessedUserSessionSequence(event *models.Event) error { - return v.saveCurrentSequence(userSessionTable, event) -} - -func (v *View) UpdateUserSessionSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(userSessionTable, instanceIDs) -} - -func (v *View) GetLatestUserSessionFailedEvent(sequence uint64, instanceID string) (*repository.FailedEvent, error) { - return v.latestFailedEvent(userSessionTable, instanceID, sequence) -} - -func (v *View) ProcessedUserSessionFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) +func (v *View) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) { + q := &query.CurrentStateSearchQueries{ + Queries: make([]query.SearchQuery, 2), + } + q.Queries[0], err = query.NewCurrentStatesInstanceIDSearchQuery(instanceID) + if err != nil { + return nil, err + } + q.Queries[1], err = query.NewCurrentStatesProjectionSearchQuery(userSessionTable) + if err != nil { + return nil, err + } + states, err := v.query.SearchCurrentStates(ctx, q) + if err != nil || states.SearchResponse.Count == 0 { + return nil, err + } + return states.CurrentStates[0], nil } diff --git a/internal/auth/repository/eventsourcing/view/view.go b/internal/auth/repository/eventsourcing/view/view.go index 08a9014d1..56e2676b8 100644 --- a/internal/auth/repository/eventsourcing/view/view.go +++ b/internal/auth/repository/eventsourcing/view/view.go @@ -8,32 +8,29 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" - eventstore "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/id" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" ) type View struct { Db *gorm.DB - keyAlgorithm crypto.EncryptionAlgorithm - idGenerator id.Generator - query *query.Queries - es eventstore.Eventstore client *database.DB + keyAlgorithm crypto.EncryptionAlgorithm + query *query.Queries + es *eventstore.Eventstore } -func StartView(sqlClient *database.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, idGenerator id.Generator, es eventstore.Eventstore) (*View, error) { +func StartView(sqlClient *database.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, es *eventstore.Eventstore) (*View, error) { gorm, err := gorm.Open("postgres", sqlClient.DB) if err != nil { return nil, err } return &View{ Db: gorm, + client: sqlClient, keyAlgorithm: keyAlgorithm, - idGenerator: idGenerator, query: queries, es: es, - client: sqlClient, }, nil } diff --git a/internal/auth/repository/token.go b/internal/auth/repository/token.go index a448a5f4e..5a4d4fae7 100644 --- a/internal/auth/repository/token.go +++ b/internal/auth/repository/token.go @@ -7,6 +7,5 @@ import ( ) type TokenRepository interface { - IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) TokenByIDs(ctx context.Context, userID, tokenID string) (*usr_model.TokenView, error) } diff --git a/internal/authz/authz.go b/internal/authz/authz.go index 6106d8a4e..cfd034de1 100644 --- a/internal/authz/authz.go +++ b/internal/authz/authz.go @@ -5,9 +5,10 @@ import ( "github.com/zitadel/zitadel/internal/authz/repository/eventsourcing" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" ) -func Start(queries *query.Queries, dbClient *database.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, externalSecure, allowOrderByCreationDate bool) (repository.Repository, error) { - return eventsourcing.Start(queries, dbClient, keyEncryptionAlgorithm, externalSecure, allowOrderByCreationDate) +func Start(queries *query.Queries, es *eventstore.Eventstore, dbClient *database.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, externalSecure bool) (repository.Repository, error) { + return eventsourcing.Start(queries, es, dbClient, keyEncryptionAlgorithm, externalSecure) } diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index f8ca3bf76..d67b2dcc7 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -19,8 +19,7 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/telemetry/tracing" usr_model "github.com/zitadel/zitadel/internal/user/model" @@ -30,7 +29,7 @@ import ( type TokenVerifierRepo struct { TokenVerificationKey crypto.EncryptionAlgorithm - Eventstore v1.Eventstore + Eventstore *eventstore.Eventstore View *view.View Query *query.Queries ExternalSecure bool @@ -48,7 +47,7 @@ func (repo *TokenVerifierRepo) tokenByID(ctx context.Context, tokenID, userID st // always load the latest sequence first, so in case the token was not found by id, // the sequence will be equal or lower than the actual projection and no events are lost - sequence, err := repo.View.GetLatestTokenSequence(ctx, instanceID) + sequence, err := repo.View.GetLatestState(ctx) logging.WithFields("instanceID", instanceID, "userID", userID, "tokenID", tokenID). OnError(err). Errorf("could not get current sequence for token check") @@ -62,7 +61,7 @@ func (repo *TokenVerifierRepo) tokenByID(ctx context.Context, tokenID, userID st token.ID = tokenID token.UserID = userID if sequence != nil { - token.Sequence = sequence.CurrentSequence + token.Sequence = sequence.Sequence } } @@ -245,14 +244,14 @@ func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName str return clientID, app.ProjectID, nil } -func (repo *TokenVerifierRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []models.EventType) (_ []*models.Event, err error) { +func (repo *TokenVerifierRepo) getUserEvents(ctx context.Context, userID, instanceID string, sequence uint64, eventTypes []eventstore.EventType) (_ []eventstore.Event, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() query, err := usr_view.UserByIDQuery(userID, instanceID, sequence, eventTypes) if err != nil { return nil, err } - return repo.Eventstore.FilterEvents(ctx, query) + return repo.Eventstore.Filter(ctx, query) } // getTokenIDAndSubject returns the TokenID and Subject of both opaque tokens and JWTs diff --git a/internal/authz/repository/eventsourcing/eventstore/user_membership.go b/internal/authz/repository/eventsourcing/eventstore/user_membership.go index ae9cea03d..d4a86a680 100644 --- a/internal/authz/repository/eventsourcing/eventstore/user_membership.go +++ b/internal/authz/repository/eventsourcing/eventstore/user_membership.go @@ -12,17 +12,18 @@ type UserMembershipRepo struct { Queries *query.Queries } -func (repo *UserMembershipRepo) SearchMyMemberships(ctx context.Context, orgID string) (_ []*authz.Membership, err error) { +func (repo *UserMembershipRepo) SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) (_ []*authz.Membership, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - memberships, err := repo.searchUserMemberships(ctx, orgID) + + memberships, err := repo.searchUserMemberships(ctx, orgID, shouldTriggerBulk) if err != nil { return nil, err } return userMembershipsToMemberships(memberships), nil } -func (repo *UserMembershipRepo) searchUserMemberships(ctx context.Context, orgID string) (_ []*query.Membership, err error) { +func (repo *UserMembershipRepo) searchUserMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) (_ []*query.Membership, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() ctxData := authz.GetCtxData(ctx) @@ -40,7 +41,7 @@ func (repo *UserMembershipRepo) searchUserMemberships(ctx context.Context, orgID } memberships, err := repo.Queries.Memberships(ctx, &query.MembershipSearchQuery{ Queries: []query.SearchQuery{userIDQuery, query.Or(orgIDsQuery, grantedIDQuery)}, - }, false) + }, false, shouldTriggerBulk) if err != nil { return nil, err } diff --git a/internal/authz/repository/eventsourcing/repository.go b/internal/authz/repository/eventsourcing/repository.go index 2df593f11..66a9a59fd 100644 --- a/internal/authz/repository/eventsourcing/repository.go +++ b/internal/authz/repository/eventsourcing/repository.go @@ -4,37 +4,30 @@ import ( "context" "github.com/zitadel/zitadel/internal/authz/repository" - "github.com/zitadel/zitadel/internal/authz/repository/eventsourcing/eventstore" + authz_es "github.com/zitadel/zitadel/internal/authz/repository/eventsourcing/eventstore" authz_view "github.com/zitadel/zitadel/internal/authz/repository/eventsourcing/view" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/id" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" ) type EsRepository struct { - eventstore.UserMembershipRepo - eventstore.TokenVerifierRepo + authz_es.UserMembershipRepo + authz_es.TokenVerifierRepo } -func Start(queries *query.Queries, dbClient *database.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, externalSecure, allowOrderByCreationDate bool) (repository.Repository, error) { - es, err := v1.Start(dbClient, allowOrderByCreationDate) - if err != nil { - return nil, err - } - - idGenerator := id.SonyFlakeGenerator() - view, err := authz_view.StartView(dbClient, idGenerator, queries) +func Start(queries *query.Queries, es *eventstore.Eventstore, dbClient *database.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, externalSecure bool) (repository.Repository, error) { + view, err := authz_view.StartView(dbClient, queries) if err != nil { return nil, err } return &EsRepository{ - eventstore.UserMembershipRepo{ + authz_es.UserMembershipRepo{ Queries: queries, }, - eventstore.TokenVerifierRepo{ + authz_es.TokenVerifierRepo{ TokenVerificationKey: keyEncryptionAlgorithm, Eventstore: es, View: view, diff --git a/internal/authz/repository/eventsourcing/view/error_event.go b/internal/authz/repository/eventsourcing/view/error_event.go deleted file mode 100644 index d343d5820..000000000 --- a/internal/authz/repository/eventsourcing/view/error_event.go +++ /dev/null @@ -1,17 +0,0 @@ -package view - -import ( - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - errTable = "auth.failed_events" -) - -func (v *View) saveFailedEvent(failedEvent *repository.FailedEvent) error { - return repository.SaveFailedEvent(v.Db, errTable, failedEvent) -} - -func (v *View) latestFailedEvent(viewName, instanceID string, sequence uint64) (*repository.FailedEvent, error) { - return repository.LatestFailedEvent(v.Db, errTable, viewName, instanceID, sequence) -} diff --git a/internal/authz/repository/eventsourcing/view/sequence.go b/internal/authz/repository/eventsourcing/view/sequence.go deleted file mode 100644 index 18b577c0c..000000000 --- a/internal/authz/repository/eventsourcing/view/sequence.go +++ /dev/null @@ -1,20 +0,0 @@ -package view - -import ( - "context" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - sequencesTable = "auth.current_sequences" -) - -func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, event.InstanceID, event.Sequence, event.CreationDate) -} - -func (v *View) latestSequence(ctx context.Context, viewName, instanceID string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, v.TimeTravel(ctx, sequencesTable), viewName, instanceID) -} diff --git a/internal/authz/repository/eventsourcing/view/token.go b/internal/authz/repository/eventsourcing/view/token.go index cfd0c8b9c..20baf2274 100644 --- a/internal/authz/repository/eventsourcing/view/token.go +++ b/internal/authz/repository/eventsourcing/view/token.go @@ -3,11 +3,12 @@ package view import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/query" usr_view "github.com/zitadel/zitadel/internal/user/repository/view" usr_view_model "github.com/zitadel/zitadel/internal/user/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" ) const ( @@ -19,11 +20,7 @@ func (v *View) TokenByIDs(tokenID, userID, instanceID string) (*usr_view_model.T } func (v *View) PutToken(token *usr_view_model.TokenView, event *models.Event) error { - err := usr_view.PutToken(v.Db, tokenTable, token) - if err != nil { - return err - } - return v.ProcessedTokenSequence(event) + return usr_view.PutToken(v.Db, tokenTable, token) } func (v *View) DeleteToken(tokenID, instanceID string, event *models.Event) error { @@ -31,7 +28,7 @@ func (v *View) DeleteToken(tokenID, instanceID string, event *models.Event) erro if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } func (v *View) DeleteSessionTokens(agentID, userID, instanceID string, event *models.Event) error { @@ -39,13 +36,24 @@ func (v *View) DeleteSessionTokens(agentID, userID, instanceID string, event *mo if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedTokenSequence(event) + return nil } -func (v *View) GetLatestTokenSequence(ctx context.Context, instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(ctx, tokenTable, instanceID) -} - -func (v *View) ProcessedTokenSequence(event *models.Event) error { - return v.saveCurrentSequence(tokenTable, event) +func (v *View) GetLatestState(ctx context.Context) (_ *query.CurrentState, err error) { + q := &query.CurrentStateSearchQueries{ + Queries: make([]query.SearchQuery, 2), + } + q.Queries[0], err = query.NewCurrentStatesInstanceIDSearchQuery(authz.GetInstance(ctx).InstanceID()) + if err != nil { + return nil, err + } + q.Queries[1], err = query.NewCurrentStatesProjectionSearchQuery(tokenTable) + if err != nil { + return nil, err + } + states, err := v.Query.SearchCurrentStates(ctx, q) + if err != nil || states.SearchResponse.Count == 0 { + return nil, err + } + return states.CurrentStates[0], nil } diff --git a/internal/authz/repository/eventsourcing/view/view.go b/internal/authz/repository/eventsourcing/view/view.go index 0b07cd2e0..f25b764f5 100644 --- a/internal/authz/repository/eventsourcing/view/view.go +++ b/internal/authz/repository/eventsourcing/view/view.go @@ -7,27 +7,24 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/query" ) type View struct { - Db *gorm.DB - Query *query.Queries - idGenerator id.Generator - client *database.DB + Db *gorm.DB + client *database.DB + Query *query.Queries } -func StartView(sqlClient *database.DB, idGenerator id.Generator, queries *query.Queries) (*View, error) { +func StartView(sqlClient *database.DB, queries *query.Queries) (*View, error) { gorm, err := gorm.Open("postgres", sqlClient.DB) if err != nil { return nil, err } return &View{ - Db: gorm, - idGenerator: idGenerator, - Query: queries, - client: sqlClient, + Db: gorm, + Query: queries, + client: sqlClient, }, nil } diff --git a/internal/authz/repository/user_membership.go b/internal/authz/repository/user_membership.go index a1b1fc78b..9e5784210 100644 --- a/internal/authz/repository/user_membership.go +++ b/internal/authz/repository/user_membership.go @@ -7,5 +7,5 @@ import ( ) type UserMembershipRepository interface { - SearchMyMemberships(ctx context.Context, orgID string) ([]*authz.Membership, error) + SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) ([]*authz.Membership, error) } diff --git a/internal/command/auth_request_test.go b/internal/command/auth_request_test.go index 9e6bf8328..0c1bb1cf3 100644 --- a/internal/command/auth_request_test.go +++ b/internal/command/auth_request_test.go @@ -13,7 +13,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/authrequest" @@ -77,29 +76,26 @@ func TestCommands_AddAuthRequest(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, - "loginClient", - "clientID", - "redirectURI", - "state", - "nonce", - []string{"openid"}, - []string{"audience"}, - domain.OIDCResponseTypeCode, - &domain.OIDCCodeChallenge{ - Challenge: "challenge", - Method: domain.CodeChallengeMethodS256, - }, - []domain.Prompt{domain.PromptNone}, - []string{"en", "de"}, - gu.Ptr(time.Duration(0)), - gu.Ptr("loginHint"), - gu.Ptr("hintUserID"), - ), - ), - }), + authrequest.NewAddedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, + "loginClient", + "clientID", + "redirectURI", + "state", + "nonce", + []string{"openid"}, + []string{"audience"}, + domain.OIDCResponseTypeCode, + &domain.OIDCCodeChallenge{ + Challenge: "challenge", + Method: domain.CodeChallengeMethodS256, + }, + []domain.Prompt{domain.PromptNone}, + []string{"en", "de"}, + gu.Ptr(time.Duration(0)), + gu.Ptr("loginHint"), + gu.Ptr("hintUserID"), + ), + ), ), idGenerator: mock.NewIDGeneratorExpectIDs(t, "id"), }, @@ -456,15 +452,13 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), ), expectPush( - []*repository.Event{eventFromEventPusherWithInstanceID( - "instanceID", - authrequest.NewSessionLinkedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, - "sessionID", - "userID", - testNow, - []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, - ), - )}), + authrequest.NewSessionLinkedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, + "sessionID", + "userID", + testNow, + []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, + ), + ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { return nil @@ -535,15 +529,13 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), ), expectPush( - []*repository.Event{eventFromEventPusherWithInstanceID( - "instanceID", - authrequest.NewSessionLinkedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, - "sessionID", - "userID", - testNow, - []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, - ), - )}), + authrequest.NewSessionLinkedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, + "sessionID", + "userID", + testNow, + []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, + ), + ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { return nil @@ -659,11 +651,9 @@ func TestCommands_FailAuthRequest(t *testing.T) { ), ), expectPush( - []*repository.Event{eventFromEventPusherWithInstanceID( - "instanceID", - authrequest.NewFailedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, - domain.OIDCErrorReasonLoginRequired), - )}), + authrequest.NewFailedEvent(mockCtx, &authrequest.NewAggregate("V2_id", "instanceID").Aggregate, + domain.OIDCErrorReasonLoginRequired), + ), ), }, args{ @@ -802,11 +792,7 @@ func TestCommands_AddAuthRequestCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - authrequest.NewCodeAddedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), - ), - }, + authrequest.NewCodeAddedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), ), ), }, @@ -937,11 +923,7 @@ func TestCommands_ExchangeAuthCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - authrequest.NewCodeExchangedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), - ), - }, + authrequest.NewCodeExchangedEvent(mockCtx, &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), ), ), }, diff --git a/internal/command/converter.go b/internal/command/converter.go index 563500d61..0761f5e19 100644 --- a/internal/command/converter.go +++ b/internal/command/converter.go @@ -16,7 +16,7 @@ func writeModelToObjectDetails(writeModel *eventstore.WriteModel) *domain.Object func pushedEventsToObjectDetails(events []eventstore.Event) *domain.ObjectDetails { return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, } } diff --git a/internal/command/device_auth_test.go b/internal/command/device_auth_test.go index d0d3dd828..7299245d4 100644 --- a/internal/command/device_auth_test.go +++ b/internal/command/device_auth_test.go @@ -14,7 +14,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/deviceauth" @@ -73,16 +72,12 @@ func TestCommands_AddDeviceAuth(t *testing.T) { name: "success", fields: fields{ eventstore: eventstoreExpect(t, expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instance1", deviceauth.NewAddedEvent( - ctx, - deviceauth.NewAggregate("1999", "instance1"), - "client_id", "123", "456", now, - []string{"a", "b", "c"}, - )), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[0]), - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[1]), + deviceauth.NewAddedEvent( + ctx, + deviceauth.NewAggregate("1999", "instance1"), + "client_id", "123", "456", now, + []string{"a", "b", "c"}, + ), )), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "1999"), }, @@ -103,17 +98,13 @@ func TestCommands_AddDeviceAuth(t *testing.T) { name: "push error", fields: fields{ eventstore: eventstoreExpect(t, expectPushFailed(pushErr, - []*repository.Event{ - eventFromEventPusherWithInstanceID("instance1", deviceauth.NewAddedEvent( - ctx, - deviceauth.NewAggregate("1999", "instance1"), - "client_id", "123", "456", now, - []string{"a", "b", "c"}, - )), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[0]), - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[1]), - )), + deviceauth.NewAddedEvent( + ctx, + deviceauth.NewAggregate("1999", "instance1"), + "client_id", "123", "456", now, + []string{"a", "b", "c"}, + )), + ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "1999"), }, args: args{ @@ -135,8 +126,8 @@ func TestCommands_AddDeviceAuth(t *testing.T) { } gotID, gotDetails, err := c.AddDeviceAuth(tt.args.ctx, tt.args.clientID, tt.args.deviceCode, tt.args.userCode, tt.args.expires, tt.args.scopes) require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, gotID, tt.wantID) - assert.Equal(t, gotDetails, tt.wantDetails) + assert.Equal(t, tt.wantID, gotID) + assert.Equal(t, tt.wantDetails, gotDetails) }) } } @@ -201,11 +192,9 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) { ), )), expectPushFailed(pushErr, - []*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewApprovedEvent( - ctx, deviceauth.NewAggregate("1999", "instance1"), "subj", - ), - )}, + deviceauth.NewApprovedEvent( + ctx, deviceauth.NewAggregate("1999", "instance1"), "subj", + ), ), ), }, @@ -225,11 +214,11 @@ func TestCommands_ApproveDeviceAuth(t *testing.T) { []string{"a", "b", "c"}, ), )), - expectPush([]*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewApprovedEvent( + expectPush( + deviceauth.NewApprovedEvent( ctx, deviceauth.NewAggregate("1999", "instance1"), "subj", ), - )}), + ), ), }, args: args{ctx, "1999", "subj"}, @@ -310,12 +299,10 @@ func TestCommands_CancelDeviceAuth(t *testing.T) { ), )), expectPushFailed(pushErr, - []*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewCanceledEvent( - ctx, deviceauth.NewAggregate("1999", "instance1"), - domain.DeviceAuthCanceledDenied, - ), - )}, + deviceauth.NewCanceledEvent( + ctx, deviceauth.NewAggregate("1999", "instance1"), + domain.DeviceAuthCanceledDenied, + ), ), ), }, @@ -335,12 +322,12 @@ func TestCommands_CancelDeviceAuth(t *testing.T) { []string{"a", "b", "c"}, ), )), - expectPush([]*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewCanceledEvent( + expectPush( + deviceauth.NewCanceledEvent( ctx, deviceauth.NewAggregate("1999", "instance1"), domain.DeviceAuthCanceledDenied, ), - )}), + ), ), }, args: args{ctx, "1999", domain.DeviceAuthCanceledDenied}, @@ -361,12 +348,12 @@ func TestCommands_CancelDeviceAuth(t *testing.T) { []string{"a", "b", "c"}, ), )), - expectPush([]*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewCanceledEvent( + expectPush( + deviceauth.NewCanceledEvent( ctx, deviceauth.NewAggregate("1999", "instance1"), domain.DeviceAuthCanceledExpired, ), - )}), + ), ), }, args: args{ctx, "1999", domain.DeviceAuthCanceledExpired}, @@ -423,14 +410,10 @@ func TestCommands_RemoveDeviceAuth(t *testing.T) { ), )), expectPushFailed(pushErr, - []*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewRemovedEvent( - ctx, deviceauth.NewAggregate("1999", "instance1"), - "client_id", "123", "456", - ), - )}, - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[0]), - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[1]), + deviceauth.NewRemovedEvent( + ctx, deviceauth.NewAggregate("1999", "instance1"), + "client_id", "123", "456", + ), ), ), }, @@ -451,14 +434,10 @@ func TestCommands_RemoveDeviceAuth(t *testing.T) { ), )), expectPush( - []*repository.Event{eventFromEventPusherWithInstanceID( - "instance1", deviceauth.NewRemovedEvent( - ctx, deviceauth.NewAggregate("1999", "instance1"), - "client_id", "123", "456", - ), - )}, - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[0]), - uniqueConstraintsFromEventConstraintWithInstanceID("instance1", unique[1]), + deviceauth.NewRemovedEvent( + ctx, deviceauth.NewAggregate("1999", "instance1"), + "client_id", "123", "456", + ), ), ), }, diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index aeffe72eb..4f5b35c33 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -165,19 +165,17 @@ func TestCommands_CreateIntent(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - success, _ := url.Parse("https://success.url") - failure, _ := url.Parse("https://failure.url") - return idpintent.NewStartedEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - success, - failure, - "idp", - ) - }(), - ), + func() eventstore.Command { + success, _ := url.Parse("https://success.url") + failure, _ := url.Parse("https://failure.url") + return idpintent.NewStartedEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + success, + failure, + "idp", + ) + }(), ), ), idGenerator: mock.ExpectID(t, "id"), @@ -545,13 +543,13 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { ), ), expectRandomPush( - eventPusherToEvents( + []eventstore.Command{ idpintent.NewSAMLRequestEvent( context.Background(), &idpintent.NewAggregate("id", "ro").Aggregate, "request", ), - ), + }, ), ), }, @@ -667,8 +665,8 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewSucceededEvent( + func() eventstore.Command { + event := idpintent.NewSucceededEvent( context.Background(), &idpintent.NewAggregate("id", "ro").Aggregate, []byte(`{"sub":"id","preferred_username":"username"}`), @@ -682,8 +680,9 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { Crypted: []byte("accessToken"), }, "idToken", - ), - ), + ) + return event + }(), ), ), }, @@ -768,21 +767,19 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewSAMLSucceededEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - []byte(`{"sub":"id","preferred_username":"username"}`), - "id", - "username", - "", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte(""), - }, - ), + idpintent.NewSAMLSucceededEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + []byte(`{"sub":"id","preferred_username":"username"}`), + "id", + "username", + "", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte(""), + }, ), ), ), @@ -808,21 +805,19 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewSAMLSucceededEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - []byte(`{"sub":"id","preferred_username":"username"}`), - "id", - "username", - "user", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte(""), - }, - ), + idpintent.NewSAMLSucceededEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + []byte(`{"sub":"id","preferred_username":"username"}`), + "id", + "username", + "user", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte(""), + }, ), ), ), @@ -880,12 +875,10 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) { fields{ eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewSAMLRequestEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - "request", - ), + idpintent.NewSAMLRequestEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + "request", ), ), ), @@ -955,16 +948,14 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewLDAPSucceededEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - []byte(`{"id":"id","preferredUsername":"username","preferredLanguage":"und"}`), - "id", - "username", - "", - map[string][]string{"id": {"id"}}, - ), + idpintent.NewLDAPSucceededEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + []byte(`{"id":"id","preferredUsername":"username","preferredLanguage":"und"}`), + "id", + "username", + "", + map[string][]string{"id": {"id"}}, ), ), ), @@ -1030,12 +1021,10 @@ func TestCommands_FailIDPIntent(t *testing.T) { fields{ eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - idpintent.NewFailedEvent( - context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, - "reason", - ), + idpintent.NewFailedEvent( + context.Background(), + &idpintent.NewAggregate("id", "ro").Aggregate, + "reason", ), ), ), diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 2736087de..b4d932fb2 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -2027,7 +2027,7 @@ func (wm *IDPTypeWriteModel) Reduce() error { return wm.WriteModel.Reduce() } -func (wm *IDPTypeWriteModel) reduceAdded(id string, t domain.IDPType, agg eventstore.Aggregate) { +func (wm *IDPTypeWriteModel) reduceAdded(id string, t domain.IDPType, agg *eventstore.Aggregate) { if wm.ID != id { return } diff --git a/internal/command/instance.go b/internal/command/instance.go index a68645237..f82252f5a 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -159,10 +159,8 @@ func (s *InstanceSetup) generateIDs(idGenerator id.Generator) (err error) { } s.zitadel.consoleAppID, err = idGenerator.Next() - if err != nil { - return err - } - return nil + + return err } func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (string, string, *MachineKey, *domain.ObjectDetails, error) { @@ -171,10 +169,6 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str return "", "", nil, nil, err } - if err = c.eventstore.NewInstance(ctx, instanceID); err != nil { - return "", "", nil, nil, err - } - ctx = authz.SetCtxData(authz.WithRequestedDomain(authz.WithInstanceID(ctx, instanceID), c.externalDomain), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID}) orgID, err := c.idGenerator.Next() @@ -464,7 +458,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str return instanceID, token, machineKey, &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: orgID, }, nil } @@ -695,7 +689,7 @@ func (c *Commands) RemoveInstance(ctx context.Context, id string) (*domain.Objec return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/instance_custom_login_text_test.go b/internal/command/instance_custom_login_text_test.go index e3fbe1119..fdb47dff1 100644 --- a/internal/command/instance_custom_login_text_test.go +++ b/internal/command/instance_custom_login_text_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -55,1328 +54,666 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, - ), - ), - }, + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, + ), ), ), }, @@ -3008,1328 +2345,666 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English, - ), - ), - }, + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, + ), + instance.NewCustomTextRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English, + ), ), ), }, @@ -7029,1328 +5704,666 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, - ), - ), - }, + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, + ), ), ), }, diff --git a/internal/command/instance_custom_message_text_test.go b/internal/command/instance_custom_message_text_test.go index f09773ed2..b1c8539ca 100644 --- a/internal/command/instance_custom_message_text_test.go +++ b/internal/command/instance_custom_message_text_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/text/language" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -56,76 +56,60 @@ func TestCommandSide_SetDefaultMessageText(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageGreeting, - "Greeting", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageSubject, - "Subject", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageTitle, - "Title", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessagePreHeader, - "PreHeader", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageText, - "Text", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageButtonText, - "ButtonText", - language.English, - ), - ), - eventFromEventPusher( - instance.NewCustomTextSetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "Template", - domain.MessageFooterText, - "Footer", - language.English, - ), - ), - }, + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageGreeting, + "Greeting", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageSubject, + "Subject", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageTitle, + "Title", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessagePreHeader, + "PreHeader", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageText, + "Text", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageButtonText, + "ButtonText", + language.English, + ), + instance.NewCustomTextSetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "Template", + domain.MessageFooterText, + "Footer", + language.English, + ), ), ), }, args: args{ - ctx: context.Background(), + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), instanceID: "INSTANCE", config: &domain.CustomMessageText{ MessageTextType: "Template", @@ -157,10 +141,9 @@ func TestCommandSide_SetDefaultMessageText(t *testing.T) { } if tt.res.err != nil && !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) + t.FailNow() } - if tt.res.err == nil { - assert.Equal(t, tt.res.want, got) - } + assert.Equal(t, tt.res.want, got) }) } } diff --git a/internal/command/instance_debug_notification_file_model.go b/internal/command/instance_debug_notification_file_model.go index c07d9a79f..c30cb7bbb 100644 --- a/internal/command/instance_debug_notification_file_model.go +++ b/internal/command/instance_debug_notification_file_model.go @@ -5,9 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/settings" - "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/settings" ) type InstanceDebugNotificationFileWriteModel struct { @@ -15,11 +14,13 @@ type InstanceDebugNotificationFileWriteModel struct { } func NewInstanceDebugNotificationFileWriteModel(ctx context.Context) *InstanceDebugNotificationFileWriteModel { + instanceID := authz.GetInstance(ctx).InstanceID() return &InstanceDebugNotificationFileWriteModel{ DebugNotificationWriteModel{ WriteModel: eventstore.WriteModel{ - AggregateID: authz.GetInstance(ctx).InstanceID(), - ResourceOwner: authz.GetInstance(ctx).InstanceID(), + AggregateID: instanceID, + ResourceOwner: instanceID, + InstanceID: instanceID, }, }, } diff --git a/internal/command/instance_debug_notification_file_test.go b/internal/command/instance_debug_notification_file_test.go index 375f45b14..10c75e193 100644 --- a/internal/command/instance_debug_notification_file_test.go +++ b/internal/command/instance_debug_notification_file_test.go @@ -4,16 +4,15 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/notification/channels/fs" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/settings" - - "github.com/stretchr/testify/assert" ) func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) { @@ -28,6 +27,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) { want *domain.ObjectDetails err func(error) bool } + ctx := authz.WithInstanceID(context.Background(), "INSTANCE") tests := []struct { name string fields fields @@ -41,7 +41,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) { t, expectFilter( eventFromEventPusher( - instance.NewDebugNotificationProviderFileAddedEvent(context.Background(), + instance.NewDebugNotificationProviderFileAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), @@ -50,7 +50,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: ctx, provider: &fs.Config{ Compact: true, Enabled: true, @@ -67,20 +67,15 @@ func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDebugNotificationProviderFileAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - ), - ), - }, + instance.NewDebugNotificationProviderFileAddedEvent(ctx, + &instance.NewAggregate("INSTANCE").Aggregate, + true, + ), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: true, }, @@ -215,13 +210,8 @@ func TestCommandSide_ChangeDebugNotificationProviderFile(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newDefaultDebugNotificationFileChangedEvent(context.Background(), - false), - ), - }, + newDefaultDebugNotificationFileChangedEvent(context.Background(), + false), ), ), }, @@ -305,13 +295,8 @@ func TestCommandSide_RemoveDebugNotificationProviderFile(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDebugNotificationProviderFileRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate), - ), - }, + instance.NewDebugNotificationProviderFileRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate), ), ), }, diff --git a/internal/command/instance_debug_notification_log_model.go b/internal/command/instance_debug_notification_log_model.go index 5b65be197..429595c98 100644 --- a/internal/command/instance_debug_notification_log_model.go +++ b/internal/command/instance_debug_notification_log_model.go @@ -5,9 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/settings" - "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/settings" ) type InstanceDebugNotificationLogWriteModel struct { @@ -15,11 +14,13 @@ type InstanceDebugNotificationLogWriteModel struct { } func NewInstanceDebugNotificationLogWriteModel(ctx context.Context) *InstanceDebugNotificationLogWriteModel { + instanceID := authz.GetInstance(ctx).InstanceID() return &InstanceDebugNotificationLogWriteModel{ DebugNotificationWriteModel{ WriteModel: eventstore.WriteModel{ - AggregateID: authz.GetInstance(ctx).InstanceID(), - ResourceOwner: authz.GetInstance(ctx).InstanceID(), + AggregateID: instanceID, + ResourceOwner: instanceID, + InstanceID: instanceID, }, }, } diff --git a/internal/command/instance_debug_notification_log_test.go b/internal/command/instance_debug_notification_log_test.go index ff66ab4d6..cb7031feb 100644 --- a/internal/command/instance_debug_notification_log_test.go +++ b/internal/command/instance_debug_notification_log_test.go @@ -4,16 +4,15 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/notification/channels/fs" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/settings" - - "github.com/stretchr/testify/assert" ) func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { @@ -28,6 +27,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { want *domain.ObjectDetails err func(error) bool } + ctx := authz.WithInstanceID(context.Background(), "INSTANCE") tests := []struct { name string fields fields @@ -42,7 +42,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), + instance.NewDebugNotificationProviderLogAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), @@ -51,7 +51,7 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: true, Enabled: true, @@ -68,20 +68,15 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - ), - ), - }, + instance.NewDebugNotificationProviderLogAddedEvent(ctx, + &instance.NewAggregate("INSTANCE").Aggregate, + true, + ), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: true, }, @@ -99,20 +94,15 @@ func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - ), - ), - }, + instance.NewDebugNotificationProviderLogAddedEvent(ctx, + &instance.NewAggregate("INSTANCE").Aggregate, + true, + ), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: true, Enabled: true, @@ -156,6 +146,7 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { want *domain.ObjectDetails err func(error) bool } + ctx := authz.WithInstanceID(context.Background(), "INSTANCE") tests := []struct { name string fields fields @@ -171,7 +162,7 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: ctx, provider: &fs.Config{ Compact: true, Enabled: true, @@ -188,7 +179,7 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { t, expectFilter( eventFromEventPusher( - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), + instance.NewDebugNotificationProviderLogAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), @@ -197,7 +188,7 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: ctx, provider: &fs.Config{ Compact: true, Enabled: false, @@ -215,25 +206,20 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), + instance.NewDebugNotificationProviderLogAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newDefaultDebugNotificationLogChangedEvent(context.Background(), - false), - ), - }, + newDefaultDebugNotificationLogChangedEvent(ctx, + false), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: false, Enabled: false, @@ -252,25 +238,20 @@ func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) { t, expectFilter( eventFromEventPusher( - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), + instance.NewDebugNotificationProviderLogAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newDefaultDebugNotificationLogChangedEvent(context.Background(), - false), - ), - }, + newDefaultDebugNotificationLogChangedEvent(ctx, + false), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), provider: &fs.Config{ Compact: false, Enabled: true, @@ -313,6 +294,7 @@ func TestCommandSide_RemoveDebugNotificationProviderLog(t *testing.T) { want *domain.ObjectDetails err func(error) bool } + ctx := authz.WithInstanceID(context.Background(), "INSTANCE") tests := []struct { name string fields fields @@ -328,7 +310,7 @@ func TestCommandSide_RemoveDebugNotificationProviderLog(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: ctx, }, res: res{ err: caos_errs.IsNotFound, @@ -342,25 +324,20 @@ func TestCommandSide_RemoveDebugNotificationProviderLog(t *testing.T) { expectFilter( eventFromEventPusherWithInstanceID( "INSTANCE", - instance.NewDebugNotificationProviderLogAddedEvent(context.Background(), + instance.NewDebugNotificationProviderLogAddedEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, true, ), ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDebugNotificationProviderLogRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate), - ), - }, + instance.NewDebugNotificationProviderLogRemovedEvent(ctx, + &instance.NewAggregate("INSTANCE").Aggregate), ), ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + ctx: authz.WithInstanceID(ctx, "INSTANCE"), }, res: res{ want: &domain.ObjectDetails{ diff --git a/internal/command/instance_domain.go b/internal/command/instance_domain.go index cbf505bc7..02ff63d40 100644 --- a/internal/command/instance_domain.go +++ b/internal/command/instance_domain.go @@ -32,7 +32,7 @@ func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -50,7 +50,7 @@ func (c *Commands) SetPrimaryInstanceDomain(ctx context.Context, instanceDomain } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -68,7 +68,7 @@ func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain stri } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/instance_domain_test.go b/internal/command/instance_domain_test.go index 90a692519..abeab0f3b 100644 --- a/internal/command/instance_domain_test.go +++ b/internal/command/instance_domain_test.go @@ -9,13 +9,11 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/repository/project" - "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/project" ) func TestCommandSide_AddInstanceDomain(t *testing.T) { @@ -167,20 +165,17 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDomainAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "domain.ch", - false, - )), - eventFromEventPusherWithInstanceID( - "INSTANCE", - newOIDCAppChangedEventInstanceDomain(context.Background(), "consoleApplicationID", "projectID", "org1"), - ), - }, - uniqueConstraintsFromEventConstraint(instance.NewAddInstanceDomainUniqueConstraint("domain.ch")), + instance.NewDomainAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "domain.ch", + false, + ), + newOIDCAppChangedEventInstanceDomain( + context.Background(), + "consoleApplicationID", + "projectID", + "org1", + ), ), ), externalSecure: true, @@ -205,13 +200,11 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) { got, err := r.AddInstanceDomain(tt.args.ctx, tt.args.domain) if tt.res.err == nil { assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { + } else if !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) + return } - if tt.res.err == nil { - assert.Equal(t, tt.res.want, got) - } + assert.Equal(t, tt.res.want, got) }) } } @@ -281,14 +274,10 @@ func TestCommandSide_SetPrimaryInstanceDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDomainPrimarySetEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "domain.ch", - )), - }, + instance.NewDomainPrimarySetEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "domain.ch", + ), ), ), }, @@ -387,15 +376,10 @@ func TestCommandSide_RemoveInstanceDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDomainRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "domain.ch", - )), - }, - uniqueConstraintsFromEventConstraint(instance.NewRemoveInstanceDomainUniqueConstraint("domain.ch")), + instance.NewDomainRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "domain.ch", + ), ), ), }, @@ -458,8 +442,12 @@ func newOIDCAppChangedEventInstanceDomain(ctx context.Context, appID, projectID, project.ChangeRedirectURIs([]string{"https://test.ch", "https://domain.ch/ui/console/auth/callback"}), project.ChangePostLogoutRedirectURIs([]string{"https://test.ch/logout", "https://domain.ch/ui/console/signedout"}), } + + aggregate := project.NewAggregate(projectID, resourceOwner).Aggregate + aggregate.InstanceID = "INSTANCE" + event, _ := project.NewOIDCConfigChangedEvent(ctx, - &project.NewAggregate(projectID, resourceOwner).Aggregate, + &aggregate, appID, changes, ) diff --git a/internal/command/instance_feature_test.go b/internal/command/instance_feature_test.go index bf07ec2c6..31ccb5888 100644 --- a/internal/command/instance_feature_test.go +++ b/internal/command/instance_feature_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/feature" @@ -81,14 +80,10 @@ func TestCommands_SetBooleanInstanceFeature(t *testing.T) { eventstore: expectEventstore( expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - feature.NewSetEvent[feature.Boolean](context.Background(), &feature.NewAggregate("featureID", "instanceID").Aggregate, - feature.EventTypeFromFeature(domain.FeatureLoginDefaultOrg), - feature.Boolean{Boolean: true}, - ), - ), - }, + feature.NewSetEvent[feature.Boolean](context.Background(), &feature.NewAggregate("featureID", "instanceID").Aggregate, + feature.EventTypeFromFeature(domain.FeatureLoginDefaultOrg), + feature.Boolean{Boolean: true}, + ), ), ), idGenerator: mock.ExpectID(t, "featureID"), @@ -117,14 +112,10 @@ func TestCommands_SetBooleanInstanceFeature(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - feature.NewSetEvent[feature.Boolean](context.Background(), &feature.NewAggregate("featureID", "instanceID").Aggregate, - feature.EventTypeFromFeature(domain.FeatureLoginDefaultOrg), - feature.Boolean{Boolean: false}, - ), - ), - }, + feature.NewSetEvent[feature.Boolean](context.Background(), &feature.NewAggregate("featureID", "instanceID").Aggregate, + feature.EventTypeFromFeature(domain.FeatureLoginDefaultOrg), + feature.Boolean{Boolean: false}, + ), ), ), }, diff --git a/internal/command/instance_idp_config_model.go b/internal/command/instance_idp_config_model.go index 7661381f2..d41b68d98 100644 --- a/internal/command/instance_idp_config_model.go +++ b/internal/command/instance_idp_config_model.go @@ -21,6 +21,7 @@ func NewInstanceIDPConfigWriteModel(ctx context.Context, configID string) *Insta WriteModel: eventstore.WriteModel{ AggregateID: authz.GetInstance(ctx).InstanceID(), ResourceOwner: authz.GetInstance(ctx).InstanceID(), + InstanceID: authz.GetInstance(ctx).InstanceID(), }, ConfigID: configID, }, diff --git a/internal/command/instance_idp_config_test.go b/internal/command/instance_idp_config_test.go index f83a13490..b6ff2f4a2 100644 --- a/internal/command/instance_idp_config_test.go +++ b/internal/command/instance_idp_config_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -61,40 +60,31 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewIDPConfigAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1", - "name1", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeGoogle, - true, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewIDPOIDCConfigAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "clientid1", - "config1", - "issuer", - "authorization-endpoint", - "token-endpoint", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("secret"), - }, - domain.OIDCMappingFieldEmail, - domain.OIDCMappingFieldEmail, - "scope", - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "INSTANCE")), + instance.NewIDPConfigAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1", + "name1", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeGoogle, + true, + ), + instance.NewIDPOIDCConfigAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "clientid1", + "config1", + "issuer", + "authorization-endpoint", + "token-endpoint", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("secret"), + }, + domain.OIDCMappingFieldEmail, + domain.OIDCMappingFieldEmail, + "scope", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"), @@ -139,31 +129,22 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewIDPConfigAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1", - "name1", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeGoogle, - false, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewIDPJWTConfigAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1", - "jwt-endpoint", - "issuer", - "keys-endpoint", - "auth", - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "INSTANCE")), + instance.NewIDPConfigAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1", + "name1", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeGoogle, + false, + ), + instance.NewIDPJWTConfigAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1", + "jwt-endpoint", + "issuer", + "keys-endpoint", + "auth", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"), @@ -305,13 +286,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified, false), - ), - }, - uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "INSTANCE")), - uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name2", "INSTANCE")), + newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified, false), ), ), }, @@ -329,6 +304,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, IDPConfigID: "config1", Name: "name2", diff --git a/internal/command/instance_idp_jwt_config_test.go b/internal/command/instance_idp_jwt_config_test.go index ab67124c5..23b67d1ba 100644 --- a/internal/command/instance_idp_jwt_config_test.go +++ b/internal/command/instance_idp_jwt_config_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/instance" @@ -194,17 +193,13 @@ func TestCommandSide_ChangeDefaultIDPJWTConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultIDPJWTConfigChangedEvent(context.Background(), - "config1", - "jwt-endpoint-changed", - "issuer-changed", - "keys-endpoint-changed", - "auth-changed", - ), - ), - }, + newDefaultIDPJWTConfigChangedEvent(context.Background(), + "config1", + "jwt-endpoint-changed", + "issuer-changed", + "keys-endpoint-changed", + "auth-changed", + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -225,6 +220,7 @@ func TestCommandSide_ChangeDefaultIDPJWTConfig(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, IDPConfigID: "config1", JWTEndpoint: "jwt-endpoint-changed", diff --git a/internal/command/instance_idp_oidc_config_test.go b/internal/command/instance_idp_oidc_config_test.go index c6b074d35..c39bd442f 100644 --- a/internal/command/instance_idp_oidc_config_test.go +++ b/internal/command/instance_idp_oidc_config_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/instance" @@ -224,26 +223,22 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultIDPOIDCConfigChangedEvent(context.Background(), - "config1", - "clientid-changed", - "issuer-changed", - "authorization-endpoint-changed", - "token-endpoint-changed", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("secret-changed"), - }, - domain.OIDCMappingFieldPreferredLoginName, - domain.OIDCMappingFieldPreferredLoginName, - []string{"scope", "scope2"}, - ), - ), - }, + newDefaultIDPOIDCConfigChangedEvent(context.Background(), + "config1", + "clientid-changed", + "issuer-changed", + "authorization-endpoint-changed", + "token-endpoint-changed", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("secret-changed"), + }, + domain.OIDCMappingFieldPreferredLoginName, + domain.OIDCMappingFieldPreferredLoginName, + []string{"scope", "scope2"}, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -268,6 +263,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, IDPConfigID: "config1", ClientID: "clientid-changed", diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index a0be4798c..3e45d8266 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -15,7 +15,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/idp" @@ -188,27 +187,23 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - "idAttribute", - nil, - idp.Options{}, - )), - }, + instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -237,32 +232,28 @@ func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - "idAttribute", - []string{"user"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -556,39 +547,34 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewOAuthIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.OAuthIDPChanges{ - idp.ChangeOAuthName("new name"), - idp.ChangeOAuthClientID("clientID2"), - idp.ChangeOAuthClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeOAuthAuthorizationEndpoint("new auth"), - idp.ChangeOAuthTokenEndpoint("new token"), - idp.ChangeOAuthUserEndpoint("new user"), - idp.ChangeOAuthScopes([]string{"openid", "profile"}), - idp.ChangeOAuthIDAttribute("newAttribute"), - idp.ChangeOAuthOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewOAuthIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.OAuthIDPChanges{ + idp.ChangeOAuthName("new name"), + idp.ChangeOAuthClientID("clientID2"), + idp.ChangeOAuthClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeOAuthAuthorizationEndpoint("new auth"), + idp.ChangeOAuthTokenEndpoint("new token"), + idp.ChangeOAuthUserEndpoint("new user"), + idp.ChangeOAuthScopes([]string{"openid", "profile"}), + idp.ChangeOAuthIDAttribute("newAttribute"), + idp.ChangeOAuthOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -738,25 +724,21 @@ func TestCommandSide_AddInstanceGenericOIDCIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - false, - idp.Options{}, - )), - }, + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -782,30 +764,26 @@ func TestCommandSide_AddInstanceGenericOIDCIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{openid.ScopeOpenID}, - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewOIDCIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{openid.ScopeOpenID}, + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1024,37 +1002,32 @@ func TestCommandSide_UpdateInstanceGenericOIDCIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewOIDCIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.OIDCIDPChanges{ - idp.ChangeOIDCName("new name"), - idp.ChangeOIDCIssuer("new issuer"), - idp.ChangeOIDCClientID("clientID2"), - idp.ChangeOIDCClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeOIDCScopes([]string{"openid", "profile"}), - idp.ChangeOIDCIsIDTokenMapping(true), - idp.ChangeOIDCOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewOIDCIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.OIDCIDPChanges{ + idp.ChangeOIDCName("new name"), + idp.ChangeOIDCIssuer("new issuer"), + idp.ChangeOIDCClientID("clientID2"), + idp.ChangeOIDCClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeOIDCScopes([]string{"openid", "profile"}), + idp.ChangeOIDCIsIDTokenMapping(true), + idp.ChangeOIDCOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1216,29 +1189,24 @@ func TestCommandSide_MigrateInstanceGenericOIDCToAzureADProvider(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - "", - false, - idp.Options{}, - ) - return event - }(), - ), - }, + func() eventstore.Command { + event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1279,34 +1247,29 @@ func TestCommandSide_MigrateInstanceGenericOIDCToAzureADProvider(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - "tenant", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + event := instance.NewOIDCIDPMigratedAzureADEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1448,23 +1411,19 @@ func TestCommandSide_MigrateInstanceOIDCToGoogleIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1504,28 +1463,24 @@ func TestCommandSide_MigrateInstanceOIDCToGoogleIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewOIDCIDPMigratedGoogleEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1650,25 +1605,21 @@ func TestCommandSide_AddInstanceAzureADIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - "", - false, - idp.Options{}, - )), - }, + instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1693,30 +1644,26 @@ func TestCommandSide_AddInstanceAzureADIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - "tenant", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1914,37 +1861,32 @@ func TestCommandSide_UpdateInstanceAzureADIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewAzureADIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.AzureADIDPChanges{ - idp.ChangeAzureADName("new name"), - idp.ChangeAzureADClientID("new clientID"), - idp.ChangeAzureADClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new clientSecret"), - }), - idp.ChangeAzureADScopes([]string{"openid", "profile"}), - idp.ChangeAzureADTenant("new tenant"), - idp.ChangeAzureADIsEmailVerified(true), - idp.ChangeAzureADOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewAzureADIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.AzureADIDPChanges{ + idp.ChangeAzureADName("new name"), + idp.ChangeAzureADClientID("new clientID"), + idp.ChangeAzureADClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeAzureADScopes([]string{"openid", "profile"}), + idp.ChangeAzureADTenant("new tenant"), + idp.ChangeAzureADIsEmailVerified(true), + idp.ChangeAzureADOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2053,23 +1995,19 @@ func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2093,28 +2031,24 @@ func TestCommandSide_AddInstanceGitHubIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewGitHubIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2286,35 +2220,30 @@ func TestCommandSide_UpdateInstanceGitHubIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewGitHubIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.GitHubIDPChanges{ - idp.ChangeGitHubName("new name"), - idp.ChangeGitHubClientID("new clientID"), - idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new clientSecret"), - }), - idp.ChangeGitHubScopes([]string{"openid", "profile"}), - idp.ChangeGitHubOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewGitHubIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitHubIDPChanges{ + idp.ChangeGitHubName("new name"), + idp.ChangeGitHubClientID("new clientID"), + idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeGitHubScopes([]string{"openid", "profile"}), + idp.ChangeGitHubOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2503,26 +2432,22 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - nil, - idp.Options{}, - )), - }, + instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2550,31 +2475,27 @@ func TestCommandSide_AddInstanceGitHubEnterpriseIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - []string{"user"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2841,38 +2762,33 @@ func TestCommandSide_UpdateInstanceGitHubEnterpriseIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.GitHubEnterpriseIDPChanges{ - idp.ChangeGitHubEnterpriseName("new name"), - idp.ChangeGitHubEnterpriseClientID("clientID2"), - idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), - idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), - idp.ChangeGitHubEnterpriseUserEndpoint("new user"), - idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), - idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitHubEnterpriseIDPChanges{ + idp.ChangeGitHubEnterpriseName("new name"), + idp.ChangeGitHubEnterpriseClientID("clientID2"), + idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), + idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), + idp.ChangeGitHubEnterpriseUserEndpoint("new user"), + idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), + idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2982,23 +2898,19 @@ func TestCommandSide_AddInstanceGitLabIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitLabIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewGitLabIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3022,28 +2934,24 @@ func TestCommandSide_AddInstanceGitLabIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitLabIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewGitLabIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3214,34 +3122,29 @@ func TestCommandSide_UpdateInstanceGitLabIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewGitLabIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.GitLabIDPChanges{ - idp.ChangeGitLabClientID("clientID2"), - idp.ChangeGitLabClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitLabScopes([]string{"openid", "profile"}), - idp.ChangeGitLabOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewGitLabIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitLabIDPChanges{ + idp.ChangeGitLabClientID("clientID2"), + idp.ChangeGitLabClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitLabScopes([]string{"openid", "profile"}), + idp.ChangeGitLabOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -3386,24 +3289,20 @@ func TestCommandSide_AddInstanceGitLabSelfHostedIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3429,29 +3328,25 @@ func TestCommandSide_AddInstanceGitLabSelfHostedIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3667,36 +3562,31 @@ func TestCommandSide_UpdateInstanceGitLabSelfHostedIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewGitLabSelfHostedIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.GitLabSelfHostedIDPChanges{ - idp.ChangeGitLabSelfHostedClientID("clientID2"), - idp.ChangeGitLabSelfHostedIssuer("newIssuer"), - idp.ChangeGitLabSelfHostedName("newName"), - idp.ChangeGitLabSelfHostedClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitLabSelfHostedScopes([]string{"openid", "profile"}), - idp.ChangeGitLabSelfHostedOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewGitLabSelfHostedIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GitLabSelfHostedIDPChanges{ + idp.ChangeGitLabSelfHostedClientID("clientID2"), + idp.ChangeGitLabSelfHostedIssuer("newIssuer"), + idp.ChangeGitLabSelfHostedName("newName"), + idp.ChangeGitLabSelfHostedClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitLabSelfHostedScopes([]string{"openid", "profile"}), + idp.ChangeGitLabSelfHostedOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -3804,23 +3694,19 @@ func TestCommandSide_AddInstanceGoogleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGoogleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewGoogleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3844,28 +3730,24 @@ func TestCommandSide_AddInstanceGoogleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewGoogleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewGoogleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4036,34 +3918,29 @@ func TestCommandSide_UpdateInstanceGoogleIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewGoogleIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.GoogleIDPChanges{ - idp.ChangeGoogleClientID("clientID2"), - idp.ChangeGoogleClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGoogleScopes([]string{"openid", "profile"}), - idp.ChangeGoogleOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewGoogleIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.GoogleIDPChanges{ + idp.ChangeGoogleClientID("clientID2"), + idp.ChangeGoogleClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGoogleScopes([]string{"openid", "profile"}), + idp.ChangeGoogleOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -4298,30 +4175,26 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - []string{"server"}, - false, - "baseDN", - "dn", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - "user", - []string{"object"}, - []string{"filter"}, - time.Second*30, - idp.LDAPAttributes{}, - idp.Options{}, - )), - }, + instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + []string{"server"}, + false, + "baseDN", + "dn", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + "user", + []string{"object"}, + []string{"filter"}, + time.Second*30, + idp.LDAPAttributes{}, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4353,49 +4226,45 @@ func TestCommandSide_AddInstanceLDAPIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - []string{"server"}, - false, - "baseDN", - "dn", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - "user", - []string{"object"}, - []string{"filter"}, - time.Second*30, - idp.LDAPAttributes{ - IDAttribute: "id", - FirstNameAttribute: "firstName", - LastNameAttribute: "lastName", - DisplayNameAttribute: "displayName", - NickNameAttribute: "nickName", - PreferredUsernameAttribute: "preferredUsername", - EmailAttribute: "email", - EmailVerifiedAttribute: "emailVerified", - PhoneAttribute: "phone", - PhoneVerifiedAttribute: "phoneVerified", - PreferredLanguageAttribute: "preferredLanguage", - AvatarURLAttribute: "avatarURL", - ProfileAttribute: "profile", - }, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewLDAPIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + []string{"server"}, + false, + "baseDN", + "dn", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + "user", + []string{"object"}, + []string{"filter"}, + time.Second*30, + idp.LDAPAttributes{ + IDAttribute: "id", + FirstNameAttribute: "firstName", + LastNameAttribute: "lastName", + DisplayNameAttribute: "displayName", + NickNameAttribute: "nickName", + PreferredUsernameAttribute: "preferredUsername", + EmailAttribute: "email", + EmailVerifiedAttribute: "emailVerified", + PhoneAttribute: "phone", + PhoneVerifiedAttribute: "phoneVerified", + PreferredLanguageAttribute: "preferredLanguage", + AvatarURLAttribute: "avatarURL", + ProfileAttribute: "profile", + }, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4742,56 +4611,51 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewLDAPIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.LDAPIDPChanges{ - idp.ChangeLDAPName("new name"), - idp.ChangeLDAPServers([]string{"new server"}), - idp.ChangeLDAPStartTLS(true), - idp.ChangeLDAPBaseDN("new basedn"), - idp.ChangeLDAPBindDN("new binddn"), - idp.ChangeLDAPBindPassword(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new password"), - }), - idp.ChangeLDAPUserBase("new user"), - idp.ChangeLDAPUserObjectClasses([]string{"new object"}), - idp.ChangeLDAPUserFilters([]string{"new filter"}), - idp.ChangeLDAPTimeout(time.Second * 20), - idp.ChangeLDAPAttributes(idp.LDAPAttributeChanges{ - IDAttribute: stringPointer("new id"), - FirstNameAttribute: stringPointer("new firstName"), - LastNameAttribute: stringPointer("new lastName"), - DisplayNameAttribute: stringPointer("new displayName"), - NickNameAttribute: stringPointer("new nickName"), - PreferredUsernameAttribute: stringPointer("new preferredUsername"), - EmailAttribute: stringPointer("new email"), - EmailVerifiedAttribute: stringPointer("new emailVerified"), - PhoneAttribute: stringPointer("new phone"), - PhoneVerifiedAttribute: stringPointer("new phoneVerified"), - PreferredLanguageAttribute: stringPointer("new preferredLanguage"), - AvatarURLAttribute: stringPointer("new avatarURL"), - ProfileAttribute: stringPointer("new profile"), - }), - idp.ChangeLDAPOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewLDAPIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.LDAPIDPChanges{ + idp.ChangeLDAPName("new name"), + idp.ChangeLDAPServers([]string{"new server"}), + idp.ChangeLDAPStartTLS(true), + idp.ChangeLDAPBaseDN("new basedn"), + idp.ChangeLDAPBindDN("new binddn"), + idp.ChangeLDAPBindPassword(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new password"), + }), + idp.ChangeLDAPUserBase("new user"), + idp.ChangeLDAPUserObjectClasses([]string{"new object"}), + idp.ChangeLDAPUserFilters([]string{"new filter"}), + idp.ChangeLDAPTimeout(time.Second * 20), + idp.ChangeLDAPAttributes(idp.LDAPAttributeChanges{ + IDAttribute: stringPointer("new id"), + FirstNameAttribute: stringPointer("new firstName"), + LastNameAttribute: stringPointer("new lastName"), + DisplayNameAttribute: stringPointer("new displayName"), + NickNameAttribute: stringPointer("new nickName"), + PreferredUsernameAttribute: stringPointer("new preferredUsername"), + EmailAttribute: stringPointer("new email"), + EmailVerifiedAttribute: stringPointer("new emailVerified"), + PhoneAttribute: stringPointer("new phone"), + PhoneVerifiedAttribute: stringPointer("new phoneVerified"), + PreferredLanguageAttribute: stringPointer("new preferredLanguage"), + AvatarURLAttribute: stringPointer("new avatarURL"), + ProfileAttribute: stringPointer("new profile"), + }), + idp.ChangeLDAPOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -4958,25 +4822,21 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewAppleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - "teamID", - "keyID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("privateKey"), - }, - nil, - idp.Options{}, - )), - }, + instance.NewAppleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + "teamID", + "keyID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("privateKey"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5002,30 +4862,26 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewAppleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "", - "clientID", - "teamID", - "keyID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("privateKey"), - }, - []string{"name", "email"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewAppleIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "", + "clientID", + "teamID", + "keyID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("privateKey"), + }, + []string{"name", "email"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5243,36 +5099,31 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewAppleIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.AppleIDPChanges{ - idp.ChangeAppleClientID("clientID2"), - idp.ChangeAppleTeamID("teamID2"), - idp.ChangeAppleKeyID("keyID2"), - idp.ChangeApplePrivateKey(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newPrivateKey"), - }), - idp.ChangeAppleScopes([]string{"name", "email"}), - idp.ChangeAppleOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewAppleIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.AppleIDPChanges{ + idp.ChangeAppleClientID("clientID2"), + idp.ChangeAppleTeamID("teamID2"), + idp.ChangeAppleKeyID("keyID2"), + idp.ChangeApplePrivateKey(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newPrivateKey"), + }), + idp.ChangeAppleScopes([]string{"name", "email"}), + idp.ChangeAppleOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -5381,25 +5232,21 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - []byte("metadata"), - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("key"), - }, - []byte("certificate"), - "", - false, - idp.Options{}, - )), - }, + instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + []byte("metadata"), + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("key"), + }, + []byte("certificate"), + "", + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5424,30 +5271,26 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - "name", - []byte("metadata"), - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("key"), - }, - []byte("certificate"), - "binding", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + "name", + []byte("metadata"), + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("key"), + }, + []byte("certificate"), + "binding", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5645,30 +5488,25 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - t := true - event, _ := instance.NewSAMLIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.SAMLIDPChanges{ - idp.ChangeSAMLName("new name"), - idp.ChangeSAMLMetadata([]byte("new metadata")), - idp.ChangeSAMLBinding("new binding"), - idp.ChangeSAMLWithSignedRequest(true), - idp.ChangeSAMLOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := instance.NewSAMLIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.SAMLIDPChanges{ + idp.ChangeSAMLName("new name"), + idp.ChangeSAMLMetadata([]byte("new metadata")), + idp.ChangeSAMLBinding("new binding"), + idp.ChangeSAMLWithSignedRequest(true), + idp.ChangeSAMLOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -5786,26 +5624,21 @@ func TestCommandSide_RegenerateInstanceSAMLProviderCertificate(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "instance1", - func() eventstore.Command { - event, _ := instance.NewSAMLIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, - "id1", - []idp.SAMLIDPChanges{ - idp.ChangeSAMLKey(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new key"), - }), - idp.ChangeSAMLCertificate([]byte("new certificate")), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + event, _ := instance.NewSAMLIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, + "id1", + []idp.SAMLIDPChanges{ + idp.ChangeSAMLKey(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new key"), + }), + idp.ChangeSAMLCertificate([]byte("new certificate")), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/instance_member_test.go b/internal/command/instance_member_test.go index 5307cbd39..384fdecae 100644 --- a/internal/command/instance_member_test.go +++ b/internal/command/instance_member_test.go @@ -7,16 +7,13 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/text/language" - "github.com/zitadel/zitadel/internal/repository/user" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" - "github.com/zitadel/zitadel/internal/repository/member" + "github.com/zitadel/zitadel/internal/repository/user" ) func TestCommandSide_AddIAMMember(t *testing.T) { @@ -159,14 +156,11 @@ func TestCommandSide_AddIAMMember(t *testing.T) { ), expectFilter(), expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusherWithInstanceID("INSTANCE", instance.NewMemberAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "user1", - []string{"IAM_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", member.NewAddMemberUniqueConstraint("INSTANCE", "user1")), + instance.NewMemberAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "user1", + []string{"IAM_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -208,16 +202,11 @@ func TestCommandSide_AddIAMMember(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewMemberAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "user1", - []string{"IAM_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", member.NewAddMemberUniqueConstraint("INSTANCE", "user1")), + instance.NewMemberAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "user1", + []string{"IAM_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -388,13 +377,11 @@ func TestCommandSide_ChangeIAMMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(instance.NewMemberChangedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "user1", - []string{"IAM_OWNER", "IAM_OWNER_VIEWER"}..., - )), - }, + instance.NewMemberChangedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "user1", + []string{"IAM_OWNER", "IAM_OWNER_VIEWER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -418,6 +405,7 @@ func TestCommandSide_ChangeIAMMember(t *testing.T) { ObjectRoot: models.ObjectRoot{ ResourceOwner: "INSTANCE", AggregateID: "INSTANCE", + InstanceID: "INSTANCE", }, UserID: "user1", Roles: []string{"IAM_OWNER", "IAM_OWNER_VIEWER"}, @@ -510,13 +498,10 @@ func TestCommandSide_RemoveIAMMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(instance.NewMemberRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "user1", - )), - }, - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("INSTANCE", "user1")), + instance.NewMemberRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "user1", + ), ), ), }, diff --git a/internal/command/instance_oidc_settings.go b/internal/command/instance_oidc_settings.go index 9ad727ebd..c44fc3b56 100644 --- a/internal/command/instance_oidc_settings.go +++ b/internal/command/instance_oidc_settings.go @@ -94,7 +94,7 @@ func (c *Commands) AddOIDCSettings(ctx context.Context, settings *domain.OIDCSet } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -112,7 +112,7 @@ func (c *Commands) ChangeOIDCSettings(ctx context.Context, settings *domain.OIDC } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/instance_oidc_settings_test.go b/internal/command/instance_oidc_settings_test.go index d40d85602..ebd04982e 100644 --- a/internal/command/instance_oidc_settings_test.go +++ b/internal/command/instance_oidc_settings_test.go @@ -8,11 +8,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -72,19 +70,14 @@ func TestCommandSide_AddOIDCConfig(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewOIDCSettingsAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - time.Hour*1, - time.Hour*1, - time.Hour*1, - time.Hour*1, - ), - ), - }, + instance.NewOIDCSettingsAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + time.Hour*1, + time.Hour*1, + time.Hour*1, + time.Hour*1, + ), ), ), }, @@ -372,16 +365,12 @@ func TestCommandSide_ChangeOIDCConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("INSTANCE", - newOIDCConfigChangedEvent( - context.Background(), - time.Hour*2, - time.Hour*2, - time.Hour*2, - time.Hour*2), - ), - }, + newOIDCConfigChangedEvent( + context.Background(), + time.Hour*2, + time.Hour*2, + time.Hour*2, + time.Hour*2), ), ), }, diff --git a/internal/command/instance_policy_domain.go b/internal/command/instance_policy_domain.go index 21fe2557b..13941b692 100644 --- a/internal/command/instance_policy_domain.go +++ b/internal/command/instance_policy_domain.go @@ -38,7 +38,7 @@ func (c *Commands) ChangeDefaultDomainPolicy(ctx context.Context, userLoginMustB // returning the values of the first event as this is the one from the instance return &domain.ObjectDetails{ Sequence: pushedEvents[0].Sequence(), - EventDate: pushedEvents[0].CreationDate(), + EventDate: pushedEvents[0].CreatedAt(), ResourceOwner: pushedEvents[0].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/instance_policy_domain_test.go b/internal/command/instance_policy_domain_test.go index e4820401e..22bca56ef 100644 --- a/internal/command/instance_policy_domain_test.go +++ b/internal/command/instance_policy_domain_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -72,17 +71,12 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewDomainPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - true, - true, - ), - ), - }, + instance.NewDomainPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + true, + true, + ), ), ), }, @@ -302,33 +296,21 @@ func TestCommandSide_ChangeDefaultDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("INSTANCE", - newDefaultDomainPolicyChangedEvent(context.Background(), false, false, false), - ), - eventFromEventPusherWithInstanceID("INSTANCE", - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "user1", - "user1@org1.com", - false, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - eventFromEventPusherWithInstanceID("INSTANCE", - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org3").Aggregate, - "user1", - "user1@org3.com", - false, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", user.NewRemoveUsernameUniqueConstraint("user1", "org1", true)), - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", user.NewAddUsernameUniqueConstraint("user1@org1.com", "org1", false)), - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", user.NewRemoveUsernameUniqueConstraint("user1", "org3", true)), - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", user.NewAddUsernameUniqueConstraint("user1@org3.com", "org3", false)), + newDefaultDomainPolicyChangedEvent(context.Background(), false, false, false), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "user1", + "user1@org1.com", + false, + user.UsernameChangedEventWithPolicyChange(), + ), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org3").Aggregate, + "user1", + "user1@org3.com", + false, + user.UsernameChangedEventWithPolicyChange(), + ), ), ), }, diff --git a/internal/command/instance_policy_label_test.go b/internal/command/instance_policy_label_test.go index aa2576a49..5b7b9dcbc 100644 --- a/internal/command/instance_policy_label_test.go +++ b/internal/command/instance_policy_label_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -98,25 +97,20 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLabelPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - true, - true, - true, - ), - ), - }, + instance.NewLabelPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + true, + true, + true, + ), ), ), }, @@ -280,23 +274,19 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultLabelPolicyChangedEvent( - context.Background(), - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - false, - false, - false), - ), - }, + newDefaultLabelPolicyChangedEvent( + context.Background(), + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + false, + false, + false), ), ), }, @@ -321,6 +311,7 @@ func TestCommandSide_ChangeDefaultLabelPolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, PrimaryColor: "#000000", BackgroundColor: "#000000", @@ -412,14 +403,9 @@ func TestCommandSide_ActivateDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLabelPolicyActivatedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - ), - ), - }, + instance.NewLabelPolicyActivatedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + ), ), ), }, @@ -559,14 +545,10 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyLogoAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "logo", - ), - ), - }, + instance.NewLabelPolicyLogoAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "logo", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -712,14 +694,10 @@ func TestCommandSide_RemoveLogoDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyLogoRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "key", - ), - ), - }, + instance.NewLabelPolicyLogoRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "key", + ), ), ), }, @@ -860,14 +838,10 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyIconAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "icon", - ), - ), - }, + instance.NewLabelPolicyIconAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "icon", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -974,14 +948,10 @@ func TestCommandSide_RemoveIconDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyIconRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "key", - ), - ), - }, + instance.NewLabelPolicyIconRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "key", + ), ), ), }, @@ -1125,14 +1095,10 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyLogoDarkAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "logo", - ), - ), - }, + instance.NewLabelPolicyLogoDarkAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "logo", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1278,14 +1244,10 @@ func TestCommandSide_RemoveLogoDarkDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyLogoDarkRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "key", - ), - ), - }, + instance.NewLabelPolicyLogoDarkRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "key", + ), ), ), }, @@ -1426,14 +1388,10 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyIconDarkAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "icon", - ), - ), - }, + instance.NewLabelPolicyIconDarkAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "icon", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1579,14 +1537,10 @@ func TestCommandSide_RemoveIconDarkDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyIconDarkRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "key", - ), - ), - }, + instance.NewLabelPolicyIconDarkRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "key", + ), ), ), }, @@ -1727,14 +1681,10 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyFontAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "font", - ), - ), - }, + instance.NewLabelPolicyFontAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "font", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1880,14 +1830,10 @@ func TestCommandSide_RemoveFontDefaultLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLabelPolicyFontRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "key", - ), - ), - }, + instance.NewLabelPolicyFontRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "key", + ), ), ), }, diff --git a/internal/command/instance_policy_login.go b/internal/command/instance_policy_login.go index d31c95f4c..6e6ce70e6 100644 --- a/internal/command/instance_policy_login.go +++ b/internal/command/instance_policy_login.go @@ -198,11 +198,7 @@ func (c *Commands) defaultLoginPolicyWriteModelByID(ctx context.Context, writeMo ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - err = c.eventstore.FilterToQueryReducer(ctx, writeModel) - if err != nil { - return err - } - return nil + return c.eventstore.FilterToQueryReducer(ctx, writeModel) } func (c *Commands) getDefaultLoginPolicy(ctx context.Context) (*domain.LoginPolicy, error) { diff --git a/internal/command/instance_policy_login_identity_provider_model.go b/internal/command/instance_policy_login_identity_provider_model.go index 019f66281..2433aa141 100644 --- a/internal/command/instance_policy_login_identity_provider_model.go +++ b/internal/command/instance_policy_login_identity_provider_model.go @@ -18,6 +18,7 @@ func NewInstanceIdentityProviderWriteModel(ctx context.Context, idpConfigID stri WriteModel: eventstore.WriteModel{ AggregateID: authz.GetInstance(ctx).InstanceID(), ResourceOwner: authz.GetInstance(ctx).InstanceID(), + InstanceID: authz.GetInstance(ctx).InstanceID(), }, IDPConfigID: idpConfigID, }, diff --git a/internal/command/instance_policy_login_test.go b/internal/command/instance_policy_login_test.go index e69c6a566..bd472da15 100644 --- a/internal/command/instance_policy_login_test.go +++ b/internal/command/instance_policy_login_test.go @@ -5,17 +5,16 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" "github.com/zitadel/zitadel/internal/repository/user" - - "github.com/stretchr/testify/assert" ) func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) { @@ -143,29 +142,24 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newDefaultLoginPolicyChangedEvent(context.Background(), - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*10, - time.Hour*20, - time.Hour*30, - time.Hour*40, - time.Hour*50), - ), - }, + newDefaultLoginPolicyChangedEvent(context.Background(), + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + domain.PasswordlessTypeNotAllowed, + "", + time.Hour*10, + time.Hour*20, + time.Hour*30, + time.Hour*40, + time.Hour*50), ), ), }, @@ -416,14 +410,9 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewIdentityProviderAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1"), - ), - }, + instance.NewIdentityProviderAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1"), ), ), }, @@ -651,13 +640,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewIdentityProviderRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1"), - ), - }, + instance.NewIdentityProviderRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1"), ), ), }, @@ -711,13 +696,9 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewIdentityProviderRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1"), - ), - }, + instance.NewIdentityProviderRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1"), ), ), }, @@ -786,18 +767,12 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewIdentityProviderRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "config1"), - ), - eventFromEventPusher( - user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "config1", "externaluser1")), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("config1", "externaluser1")), + instance.NewIdentityProviderRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "config1"), + user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "config1", "externaluser1"), ), ), }, @@ -905,14 +880,9 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeTOTP), - ), - }, + instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeTOTP), ), ), }, @@ -933,14 +903,9 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeOTPEmail), - ), - }, + instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeOTPEmail), ), ), }, @@ -961,14 +926,9 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -996,14 +956,9 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + instance.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -1187,13 +1142,9 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeTOTP), - ), - }, + instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeTOTP), ), ), }, @@ -1221,13 +1172,9 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeOTPEmail), - ), - }, + instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeOTPEmail), ), ), }, @@ -1255,13 +1202,9 @@ func TestCommandSide_RemoveSecondFactorDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + instance.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -1381,14 +1324,9 @@ func TestCommandSide_AddMultiFactorDefaultLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLoginPolicyMultiFactorAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.MultiFactorTypeU2FWithPIN), - ), - }, + instance.NewLoginPolicyMultiFactorAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.MultiFactorTypeU2FWithPIN), ), ), }, @@ -1514,13 +1452,9 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewLoginPolicyMultiFactorRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.MultiFactorTypeU2FWithPIN), - ), - }, + instance.NewLoginPolicyMultiFactorRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.MultiFactorTypeU2FWithPIN), ), ), }, diff --git a/internal/command/instance_policy_mail_template_model.go b/internal/command/instance_policy_mail_template_model.go index a7b6fcabf..489d6ebd2 100644 --- a/internal/command/instance_policy_mail_template_model.go +++ b/internal/command/instance_policy_mail_template_model.go @@ -21,6 +21,7 @@ func NewInstanceMailTemplateWriteModel(ctx context.Context) *InstanceMailTemplat WriteModel: eventstore.WriteModel{ AggregateID: authz.GetInstance(ctx).InstanceID(), ResourceOwner: authz.GetInstance(ctx).InstanceID(), + InstanceID: authz.GetInstance(ctx).InstanceID(), }, }, } diff --git a/internal/command/instance_policy_mail_template_test.go b/internal/command/instance_policy_mail_template_test.go index 0ef85dabf..3f90b1bf5 100644 --- a/internal/command/instance_policy_mail_template_test.go +++ b/internal/command/instance_policy_mail_template_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -80,15 +79,11 @@ func TestCommandSide_AddDefaultMailTemplatePolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewMailTemplateAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - []byte("template"), - ), - ), - }, + instance.NewMailTemplateAddedEvent( + authz.WithInstanceID(context.Background(), "INSTANCE"), + &instance.NewAggregate("INSTANCE").Aggregate, + []byte("template"), + ), ), ), }, @@ -219,11 +214,7 @@ func TestCommandSide_ChangeDefaultMailTemplatePolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultMailTemplatePolicyChangedEvent(context.Background(), []byte("template-change")), - ), - }, + newDefaultMailTemplatePolicyChangedEvent(context.Background(), []byte("template-change")), ), ), }, @@ -238,6 +229,7 @@ func TestCommandSide_ChangeDefaultMailTemplatePolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, Template: []byte("template-change"), }, diff --git a/internal/command/instance_policy_notification_test.go b/internal/command/instance_policy_notification_test.go index b4639ae5e..339e28fd6 100644 --- a/internal/command/instance_policy_notification_test.go +++ b/internal/command/instance_policy_notification_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -64,14 +63,10 @@ func TestCommandSide_AddDefaultNotificationPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewNotificationPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - ), - ), - }, + instance.NewNotificationPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + ), ), ), }, @@ -93,14 +88,10 @@ func TestCommandSide_AddDefaultNotificationPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewNotificationPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - ), - ), - }, + instance.NewNotificationPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + ), ), ), }, @@ -209,12 +200,9 @@ func TestCommandSide_ChangeDefaultNotificationPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultNotificationPolicyChangedEvent(context.Background(), - true, - )), - }, + newDefaultNotificationPolicyChangedEvent(context.Background(), + true, + ), ), ), }, diff --git a/internal/command/instance_policy_password_age_test.go b/internal/command/instance_policy_password_age_test.go index ac6f9bb51..e1850fc35 100644 --- a/internal/command/instance_policy_password_age_test.go +++ b/internal/command/instance_policy_password_age_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -67,16 +66,11 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewPasswordAgePolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - 365, - 10, - ), - ), - }, + instance.NewPasswordAgePolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + 365, + 10, + ), ), ), }, @@ -190,11 +184,7 @@ func TestCommandSide_ChangeDefaultPasswordAgePolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultPasswordAgePolicyChangedEvent(context.Background(), 125, 5), - ), - }, + newDefaultPasswordAgePolicyChangedEvent(context.Background(), 125, 5), ), ), }, @@ -210,6 +200,7 @@ func TestCommandSide_ChangeDefaultPasswordAgePolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, MaxAgeDays: 125, ExpireWarnDays: 5, diff --git a/internal/command/instance_policy_password_complexity_test.go b/internal/command/instance_policy_password_complexity_test.go index 3e342061b..f696341f2 100644 --- a/internal/command/instance_policy_password_complexity_test.go +++ b/internal/command/instance_policy_password_complexity_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -92,16 +91,11 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - 8, - true, true, true, true, - ), - ), - }, + instance.NewPasswordComplexityPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + 8, + true, true, true, true, + ), ), ), }, @@ -245,11 +239,7 @@ func TestCommandSide_ChangeDefaultPasswordComplexityPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultPasswordComplexityPolicyChangedEvent(context.Background(), 10, false, false, false, false), - ), - }, + newDefaultPasswordComplexityPolicyChangedEvent(context.Background(), 10, false, false, false, false), ), ), }, @@ -268,6 +258,7 @@ func TestCommandSide_ChangeDefaultPasswordComplexityPolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, MinLength: 10, HasUppercase: false, diff --git a/internal/command/instance_policy_password_lockout_test.go b/internal/command/instance_policy_password_lockout_test.go index 07a0c77cf..1eb0a651a 100644 --- a/internal/command/instance_policy_password_lockout_test.go +++ b/internal/command/instance_policy_password_lockout_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -67,16 +66,11 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewLockoutPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - 10, - true, - ), - ), - }, + instance.NewLockoutPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + 10, + true, + ), ), ), }, @@ -190,11 +184,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultLockoutPolicyChangedEvent(context.Background(), 20, false), - ), - }, + newDefaultLockoutPolicyChangedEvent(context.Background(), 20, false), ), ), }, @@ -210,6 +200,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, MaxPasswordAttempts: 20, ShowLockOutFailures: false, diff --git a/internal/command/instance_policy_privacy_test.go b/internal/command/instance_policy_privacy_test.go index eddf8a0c1..ad7bbfb15 100644 --- a/internal/command/instance_policy_privacy_test.go +++ b/internal/command/instance_policy_privacy_test.go @@ -7,11 +7,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/policy" @@ -74,18 +72,13 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewPrivacyPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "TOSLink", - "PrivacyLink", - "HelpLink", - "support@example.com", - ), - ), - }, + instance.NewPrivacyPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "TOSLink", + "PrivacyLink", + "HelpLink", + "support@example.com", + ), ), ), }, @@ -127,18 +120,13 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewPrivacyPolicyAddedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "", - "", - "", - "", - ), - ), - }, + instance.NewPrivacyPolicyAddedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "", + "", + "", + "", + ), ), ), }, @@ -282,16 +270,12 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDefaultPrivacyPolicyChangedEvent(context.Background(), - "TOSLinkChanged", - "PrivacyLinkChanged", - "HelpLinkChanged", - "support2@example.com", - ), - ), - }, + newDefaultPrivacyPolicyChangedEvent(context.Background(), + "TOSLinkChanged", + "PrivacyLinkChanged", + "HelpLinkChanged", + "support2@example.com", + ), ), ), }, @@ -309,6 +293,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { ObjectRoot: models.ObjectRoot{ AggregateID: "INSTANCE", ResourceOwner: "INSTANCE", + InstanceID: "INSTANCE", }, TOSLink: "TOSLinkChanged", PrivacyLink: "PrivacyLinkChanged", diff --git a/internal/command/instance_policy_security.go b/internal/command/instance_policy_security.go index c35094666..0222eca9f 100644 --- a/internal/command/instance_policy_security.go +++ b/internal/command/instance_policy_security.go @@ -23,7 +23,7 @@ func (c *Commands) SetSecurityPolicy(ctx context.Context, enabled bool, allowedO } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/instance_secret_generator_model.go b/internal/command/instance_secret_generator_model.go index b4122edb5..8ced3104b 100644 --- a/internal/command/instance_secret_generator_model.go +++ b/internal/command/instance_secret_generator_model.go @@ -28,6 +28,7 @@ func NewInstanceSecretGeneratorConfigWriteModel(ctx context.Context, GeneratorTy WriteModel: eventstore.WriteModel{ AggregateID: authz.GetInstance(ctx).InstanceID(), ResourceOwner: authz.GetInstance(ctx).InstanceID(), + InstanceID: authz.GetInstance(ctx).InstanceID(), }, GeneratorType: GeneratorType, } diff --git a/internal/command/instance_settings.go b/internal/command/instance_settings.go index 05684ed93..497bc99e1 100644 --- a/internal/command/instance_settings.go +++ b/internal/command/instance_settings.go @@ -25,7 +25,7 @@ func (c *Commands) AddSecretGeneratorConfig(ctx context.Context, typ domain.Secr } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: agg.ResourceOwner, }, nil } diff --git a/internal/command/instance_settings_test.go b/internal/command/instance_settings_test.go index 6b0011511..19ca9c5d4 100644 --- a/internal/command/instance_settings_test.go +++ b/internal/command/instance_settings_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -95,23 +94,17 @@ func TestCommandSide_AddSecretGenerator(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSecretGeneratorAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecretGeneratorTypeInitCode, - 4, - time.Hour*1, - true, - true, - true, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)), + instance.NewSecretGeneratorAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecretGeneratorTypeInitCode, + 4, + time.Hour*1, + true, + true, + true, + true, + ), ), ), }, @@ -193,23 +186,17 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) { eventstore: expectEventstore( expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSecretGeneratorAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecretGeneratorTypeInitCode, - 4, - time.Hour*1, - true, - true, - true, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)), + instance.NewSecretGeneratorAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecretGeneratorTypeInitCode, + 4, + time.Hour*1, + true, + true, + true, + true, + ), ), ), }, @@ -256,23 +243,17 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSecretGeneratorAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecretGeneratorTypeInitCode, - 4, - time.Hour*1, - true, - true, - true, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)), + instance.NewSecretGeneratorAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecretGeneratorTypeInitCode, + 4, + time.Hour*1, + true, + true, + true, + true, + ), ), ), }, @@ -351,19 +332,15 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("INSTANCE", - newSecretGeneratorChangedEvent(context.Background(), - domain.SecretGeneratorTypeInitCode, - 8, - time.Hour*2, - false, - false, - false, - false, - ), - ), - }, + newSecretGeneratorChangedEvent(context.Background(), + domain.SecretGeneratorTypeInitCode, + 8, + time.Hour*2, + false, + false, + false, + false, + ), ), ), }, @@ -511,14 +488,9 @@ func TestCommandSide_RemoveSecretGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewSecretGeneratorRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - domain.SecretGeneratorTypeInitCode), - ), - }, - uniqueConstraintsFromEventConstraint(instance.NewRemoveSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)), + instance.NewSecretGeneratorRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + domain.SecretGeneratorTypeInitCode), ), ), }, diff --git a/internal/command/instance_test.go b/internal/command/instance_test.go index 3d29804d0..18007637d 100644 --- a/internal/command/instance_test.go +++ b/internal/command/instance_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -133,15 +132,10 @@ func TestCommandSide_UpdateInstance(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewInstanceChangedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "INSTANCE_CHANGED", - ), - ), - }, + instance.NewInstanceChangedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "INSTANCE_CHANGED", + ), ), ), }, @@ -271,22 +265,14 @@ func TestCommandSide_RemoveInstance(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewInstanceRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "INSTANCE", - []string{ - "instance.domain", - "custom.domain", - }, - ), - ), - }, - uniqueConstraintsFromEventConstraint(instance.NewRemoveInstanceDomainUniqueConstraint("instance.domain")), - uniqueConstraintsFromEventConstraint(instance.NewRemoveInstanceDomainUniqueConstraint("custom.domain")), - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", eventstore.NewRemoveInstanceUniqueConstraints()), + instance.NewInstanceRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "INSTANCE", + []string{ + "instance.domain", + "custom.domain", + }, + ), ), ), }, diff --git a/internal/command/main_test.go b/internal/command/main_test.go index ba886e5c4..c83040948 100644 --- a/internal/command/main_test.go +++ b/internal/command/main_test.go @@ -40,7 +40,12 @@ func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore { for _, e := range expects { e(m) } - es := eventstore.NewEventstore(eventstore.TestConfig(m)) + es := eventstore.NewEventstore( + &eventstore.Config{ + Querier: m.MockQuerier, + Pusher: m.MockPusher, + }, + ) iam_repo.RegisterEventMappers(es) org.RegisterEventMappers(es) usr_repo.RegisterEventMappers(es) @@ -72,89 +77,43 @@ func eventPusherToEvents(eventsPushes ...eventstore.Command) []*repository.Event } events[i] = &repository.Event{ AggregateID: event.Aggregate().ID, - AggregateType: repository.AggregateType(event.Aggregate().Type), + AggregateType: event.Aggregate().Type, ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, - EditorService: event.EditorService(), - EditorUser: event.EditorUser(), - Type: repository.EventType(event.Type()), - Version: repository.Version(event.Aggregate().Version), + EditorUser: event.Creator(), + Typ: event.Type(), + Version: event.Aggregate().Version, Data: data, + Constraints: event.UniqueConstraints(), } } return events } -type testRepo struct { - events []*repository.Event - uniqueConstraints []*repository.UniqueConstraint - sequence uint64 - err error - t *testing.T -} - -func (repo *testRepo) Health(ctx context.Context) error { - return nil -} - -func (repo *testRepo) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - repo.events = append(repo.events, events...) - repo.uniqueConstraints = append(repo.uniqueConstraints, uniqueConstraints...) - return nil -} - -func (repo *testRepo) Filter(ctx context.Context, searchQuery *repository.SearchQuery) ([]*repository.Event, error) { - events := make([]*repository.Event, 0, len(repo.events)) - for _, event := range repo.events { - for _, filter := range searchQuery.Filters { - for _, f := range filter { - if f.Field == repository.FieldAggregateType { - if event.AggregateType != f.Value { - continue - } - } - } - } - events = append(events, event) - } - return repo.events, nil -} - -func filterAggregateType(aggregateType string) { - -} - -func (repo *testRepo) LatestSequence(ctx context.Context, queryFactory *repository.SearchQuery) (uint64, error) { - if repo.err != nil { - return 0, repo.err - } - return repo.sequence, nil -} - -func expectPush(events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect { +func expectPush(commands ...eventstore.Command) expect { return func(m *mock.MockRepository) { - m.ExpectPush(events, uniqueConstraints...) + m.ExpectPush(commands) } } -func expectPushFailed(err error, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect { +func expectPushFailed(err error, commands ...eventstore.Command) expect { return func(m *mock.MockRepository) { - m.ExpectPushFailed(err, events, uniqueConstraints...) + m.ExpectPushFailed(err, commands) } } -func expectRandomPush(events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect { +func expectRandomPush(events []eventstore.Command) expect { return func(m *mock.MockRepository) { - m.ExpectRandomPush(events, uniqueConstraints...) + m.ExpectRandomPush(events) } } -func expectRandomPushFailed(err error, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) expect { +func expectRandomPushFailed(err error, events []eventstore.Command) expect { return func(m *mock.MockRepository) { - m.ExpectRandomPushFailed(err, events, uniqueConstraints...) + m.ExpectRandomPushFailed(err, events) } } -func expectFilter(events ...*repository.Event) expect { +func expectFilter(events ...eventstore.Event) expect { return func(m *mock.MockRepository) { m.ExpectFilterEvents(events...) } @@ -180,39 +139,35 @@ func expectFilterOrgMemberNotFound() expect { func eventFromEventPusher(event eventstore.Command) *repository.Event { data, _ := eventstore.EventData(event) return &repository.Event{ - ID: "", - Sequence: 0, - PreviousAggregateSequence: 0, - PreviousAggregateTypeSequence: 0, - CreationDate: time.Time{}, - Type: repository.EventType(event.Type()), - Data: data, - EditorService: event.EditorService(), - EditorUser: event.EditorUser(), - Version: repository.Version(event.Aggregate().Version), - AggregateID: event.Aggregate().ID, - AggregateType: repository.AggregateType(event.Aggregate().Type), - ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, + InstanceID: event.Aggregate().InstanceID, + ID: "", + Seq: 0, + CreationDate: time.Time{}, + Typ: event.Type(), + Data: data, + EditorUser: event.Creator(), + Version: event.Aggregate().Version, + AggregateID: event.Aggregate().ID, + AggregateType: event.Aggregate().Type, + ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, + Constraints: event.UniqueConstraints(), } } func eventFromEventPusherWithInstanceID(instanceID string, event eventstore.Command) *repository.Event { data, _ := eventstore.EventData(event) return &repository.Event{ - ID: "", - Sequence: 0, - PreviousAggregateSequence: 0, - PreviousAggregateTypeSequence: 0, - CreationDate: time.Time{}, - Type: repository.EventType(event.Type()), - Data: data, - EditorService: event.EditorService(), - EditorUser: event.EditorUser(), - Version: repository.Version(event.Aggregate().Version), - AggregateID: event.Aggregate().ID, - AggregateType: repository.AggregateType(event.Aggregate().Type), - ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, - InstanceID: instanceID, + ID: "", + Seq: 0, + CreationDate: time.Time{}, + Typ: event.Type(), + Data: data, + EditorUser: event.Creator(), + Version: event.Aggregate().Version, + AggregateID: event.Aggregate().ID, + AggregateType: event.Aggregate().Type, + ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, + InstanceID: instanceID, } } @@ -222,23 +177,6 @@ func eventFromEventPusherWithCreationDateNow(event eventstore.Command) *reposito return e } -func uniqueConstraintsFromEventConstraint(constraint *eventstore.EventUniqueConstraint) *repository.UniqueConstraint { - return &repository.UniqueConstraint{ - UniqueType: constraint.UniqueType, - UniqueField: constraint.UniqueField, - ErrorMessage: constraint.ErrorMessage, - Action: repository.UniqueConstraintAction(constraint.Action)} -} - -func uniqueConstraintsFromEventConstraintWithInstanceID(instanceID string, constraint *eventstore.EventUniqueConstraint) *repository.UniqueConstraint { - return &repository.UniqueConstraint{ - InstanceID: instanceID, - UniqueType: constraint.UniqueType, - UniqueField: constraint.UniqueField, - ErrorMessage: constraint.ErrorMessage, - Action: repository.UniqueConstraintAction(constraint.Action)} -} - func GetMockSecretGenerator(t *testing.T) crypto.Generator { ctrl := gomock.NewController(t) alg := crypto.CreateMockEncryptionAlg(ctrl) diff --git a/internal/command/oidc_session_test.go b/internal/command/oidc_session_test.go index aba917fa2..ab850aeea 100644 --- a/internal/command/oidc_session_test.go +++ b/internal/command/oidc_session_test.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/authrequest" @@ -184,19 +183,11 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) { ), expectFilter(), // token lifetime expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), - ), - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid"}, time.Hour), - ), - eventFromEventPusherWithInstanceID("instanceID", - authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), - ), - }, + oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), + oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "at_accessTokenID", []string{"openid"}, time.Hour), + authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), ), ), idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID", "accessTokenID"), @@ -385,23 +376,13 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) { ), expectFilter(), // token lifetime expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid", "offline_access"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), - ), - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour), - ), - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour), - ), - eventFromEventPusherWithInstanceID("instanceID", - authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), - ), - }, + oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid", "offline_access"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), + oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour), + oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour), + authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), ), ), idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID", "accessTokenID", "refreshTokenID"), @@ -575,16 +556,10 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) { ), expectFilter(), // token lifetime expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour), - ), - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewRefreshTokenRenewedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "rt_refreshTokenID2", 24*time.Hour), - ), - }, + oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour), + oidcsession.NewRefreshTokenRenewedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, + "rt_refreshTokenID2", 24*time.Hour), ), ), idGenerator: mock.NewIDGeneratorExpectIDs(t, "accessTokenID", "refreshTokenID2"), @@ -906,11 +881,9 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) { "rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour), ), ), - expectPush([]*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewRefreshTokenRevokedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate), - ), - }), + expectPush( + oidcsession.NewRefreshTokenRevokedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate), + ), ), keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, @@ -985,11 +958,9 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) { "rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour), ), ), - expectPush([]*repository.Event{ - eventFromEventPusherWithInstanceID("instanceID", - oidcsession.NewAccessTokenRevokedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate), - ), - }), + expectPush( + oidcsession.NewAccessTokenRevokedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate), + ), ), keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, diff --git a/internal/command/org.go b/internal/command/org.go index 72a97b6f0..137b1a65c 100644 --- a/internal/command/org.go +++ b/internal/command/org.go @@ -167,7 +167,7 @@ func (c *orgSetupCommands) push(ctx context.Context) (_ *CreatedOrg, err error) return &CreatedOrg{ ObjectDetails: &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: c.aggregate.ID, }, CreatedAdmins: c.createdAdmins(), @@ -424,7 +424,7 @@ func (c *Commands) RemoveOrg(ctx context.Context, id string) (*domain.ObjectDeta return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/org_action_test.go b/internal/command/org_action_test.go index dadb64261..58d5d1bf6 100644 --- a/internal/command/org_action_test.go +++ b/internal/command/org_action_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/id/mock" @@ -60,18 +59,13 @@ func TestCommands_AddAction(t *testing.T) { eventstore: eventstoreExpect(t, expectPushFailed( errors.ThrowPreconditionFailed(nil, "id", "name already exists"), - []*repository.Event{ - eventFromEventPusher( - action.NewAddedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - "name", - "name() {};", - 0, - false, - ), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewAddActionNameUniqueConstraint("name", "org1")), + action.NewAddedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + "name", + "name() {};", + 0, + false, + ), ), ), idGenerator: mock.ExpectID(t, "id1"), @@ -93,18 +87,13 @@ func TestCommands_AddAction(t *testing.T) { fields{ eventstore: eventstoreExpect(t, expectPush( - []*repository.Event{ - eventFromEventPusher( - action.NewAddedEvent(context.Background(), - &action.NewAggregate("id2", "org1").Aggregate, - "name2", - "name2() {};", - 0, - false, - ), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewAddActionNameUniqueConstraint("name2", "org1")), + action.NewAddedEvent(context.Background(), + &action.NewAggregate("id2", "org1").Aggregate, + "name2", + "name2() {};", + 0, + false, + ), ), ), idGenerator: mock.ExpectID(t, "id2"), @@ -253,22 +242,16 @@ func TestCommands_ChangeAction(t *testing.T) { ), expectPushFailed( errors.ThrowPreconditionFailed(nil, "id", "name already exists"), - []*repository.Event{ - eventFromEventPusher( - func() *action.ChangedEvent { - event, _ := action.NewChangedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - []action.ActionChanges{ - action.ChangeName("name2", "name"), - action.ChangeScript("name2() {};"), - }, - ) - return event - }(), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewRemoveActionNameUniqueConstraint("name", "org1")), - uniqueConstraintsFromEventConstraint(action.NewAddActionNameUniqueConstraint("name2", "org1")), + func() *action.ChangedEvent { + event, _ := action.NewChangedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + []action.ActionChanges{ + action.ChangeName("name2", "name"), + action.ChangeScript("name2() {};"), + }, + ) + return event + }(), ), ), }, @@ -303,22 +286,16 @@ func TestCommands_ChangeAction(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - func() *action.ChangedEvent { - event, _ := action.NewChangedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - []action.ActionChanges{ - action.ChangeName("name2", "name"), - action.ChangeScript("name2() {};"), - }, - ) - return event - }(), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewRemoveActionNameUniqueConstraint("name", "org1")), - uniqueConstraintsFromEventConstraint(action.NewAddActionNameUniqueConstraint("name2", "org1")), + func() *action.ChangedEvent { + event, _ := action.NewChangedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + []action.ActionChanges{ + action.ChangeName("name2", "name"), + action.ChangeScript("name2() {};"), + }, + ) + return event + }(), ), ), }, @@ -455,13 +432,9 @@ func TestCommands_DeactivateAction(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - action.NewDeactivatedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - ), - ), - }, + action.NewDeactivatedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + ), ), ), }, @@ -592,13 +565,9 @@ func TestCommands_ReactivateAction(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - action.NewReactivatedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - ), - ), - }, + action.NewReactivatedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + ), ), ), }, @@ -699,15 +668,10 @@ func TestCommands_DeleteAction(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - action.NewRemovedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - "name", - ), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewRemoveActionNameUniqueConstraint("name", "org1")), + action.NewRemovedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + "name", + ), ), ), }, @@ -738,22 +702,15 @@ func TestCommands_DeleteAction(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - action.NewRemovedEvent(context.Background(), - &action.NewAggregate("id1", "org1").Aggregate, - "name", - ), - ), - eventFromEventPusher( - org.NewTriggerActionsCascadeRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.FlowTypeExternalAuthentication, - "id1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(action.NewRemoveActionNameUniqueConstraint("name", "org1")), + action.NewRemovedEvent(context.Background(), + &action.NewAggregate("id1", "org1").Aggregate, + "name", + ), + org.NewTriggerActionsCascadeRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.FlowTypeExternalAuthentication, + "id1", + ), ), ), }, diff --git a/internal/command/org_custom_login_text_test.go b/internal/command/org_custom_login_text_test.go index 46fdcd5a5..ef0754c26 100644 --- a/internal/command/org_custom_login_text_test.go +++ b/internal/command/org_custom_login_text_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -71,1107 +70,666 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, - ), - ), eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, - ), - ), - }, + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionLoginButtonText, "LoginButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, + ), ), ), }, @@ -2579,1103 +2137,663 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English, - ), - ), - }, + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, + ), + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English, + ), ), ), }, @@ -5924,1103 +5042,663 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, - ), - ), - }, + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitleLinkingProcess, "TitleLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountOtherUser, "OtherUser", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateActive, "SessionState0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountSessionStateInactive, "SessionState1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginTitleLinkingProcess, "TitleLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginDescriptionLinkingProcess, "DescriptionLinking", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNameLabel, "LoginNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUsernamePlaceHolder, "UsernamePlaceholder", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginLoginnamePlaceHolder, "LoginnamePlaceholder", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginRegisterButtonText, "RegisterButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginExternalUserDescription, "ExternalUserDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLoginUserMustBeMemberOfOrg, "MustBeMemberOfOrg", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetLinkText, "ResetLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordMinLength, "MinLength", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasUppercase, "HasUppercase", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasLowercase, "HasLowercase", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasNumber, "HasNumber", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordHasSymbol, "HasSymbol", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordConfirmation, "Confirmation", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyUsernameChangeDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitPasswordDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyEmailVerificationDoneLoginButtonText, "LoginButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserResendButtonText, "ResendButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitializeUserNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitUserDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptOTPOption, "Provider0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptU2FOption, "Provider1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptSkipButtonText, "SkipButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAPromptNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPDescriptionOTP, "OTPDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPSecretLabel, "SecretLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAOTPCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FTokenNameLabel, "TokenNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyInitMFADoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersChooseOther, "ChooseOther", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersOTP, "Provider0", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyMFAProvidersU2F, "Provider1", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPCodeLabel, "CodeLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAOTPNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FValidateTokenText, "ValidateTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyVerifyMFAU2FErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessLoginWithPwButtonText, "LoginWithPwButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessValidateTokenButtonText, "ValidateTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptDescriptionInit, "DescriptionInit", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptPasswordlessButtonText, "PasswordlessButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessPromptSkipButtonText, "SkipButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationRegisterTokenButtonText, "RegisterTokenButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationTokenNameLabel, "TokenNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationNotSupported, "NotSupported", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationErrorRetry, "ErrorRetry", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneDescriptionClose, "DescriptionClose", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordlessRegistrationDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeOldPasswordLabel, "OldPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordLabel, "NewPasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNewPasswordConfirmLabel, "NewPasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordChangeDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyPasswordResetDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionUserNameButtonText, "RegisterUsernamePasswordButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationOptionExternalLoginDescription, "ExternalLoginDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserDescriptionOrgRegister, "DescriptionOrgRegister", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserLanguageLabel, "LanguageLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserGenderLabel, "GenderLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegistrationUserBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNicknameLabel, "NicknameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewLanguageLabel, "LanguageLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPhoneLabel, "PhoneLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewBackButtonText, "BackButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalRegistrationUserOverviewNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgOrgNameLabel, "OrgNameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgFirstnameLabel, "FirstnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgLastnameLabel, "LastnameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgUsernameLabel, "UsernameLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgEmailLabel, "EmailLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordLabel, "PasswordLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPasswordConfirmLabel, "PasswordConfirmLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyRegisterOrgSaveButtonText, "SaveButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneCancelButtonText, "CancelButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLinkingUserDoneNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundLinkButtonText, "LinkButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundAutoRegisterButtonText, "AutoRegisterButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSAndPrivacyLabel, "TOSAndPrivacyLabel", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSConfirm, "TOSConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundTOSLinkText, "TOSLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyConfirm, "PrivacyConfirm", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyExternalNotFoundPrivacyLinkText, "PrivacyLinkText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginAutoRedirectDescription, "AutoRedirectDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginRedirectedDescription, "RedirectedDescription", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySuccessLoginNextButtonText, "NextButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneTitle, "Title", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneDescription, "Description", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyLogoutDoneLoginButtonText, "LoginButtonText", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterTOS, "TOS", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterPrivacyPolicy, "PrivacyPolicy", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, + ), + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English, + ), ), ), }, diff --git a/internal/command/org_custom_message_text_test.go b/internal/command/org_custom_message_text_test.go index ecf10e069..61a562e45 100644 --- a/internal/command/org_custom_message_text_test.go +++ b/internal/command/org_custom_message_text_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -71,71 +70,61 @@ func TestCommandSide_SetCustomMessageText(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageGreeting, - "Greeting", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageSubject, - "Subject", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageTitle, - "Title", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessagePreHeader, - "PreHeader", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageText, - "Text", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageButtonText, - "ButtonText", - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageFooterText, - "Footer", - language.English, - ), - ), - }, + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageGreeting, + "Greeting", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageSubject, + "Subject", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageTitle, + "Title", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessagePreHeader, + "PreHeader", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageText, + "Text", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageButtonText, + "ButtonText", + language.English, + ), + + org.NewCustomTextSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageFooterText, + "Footer", + language.English, + ), ), ), }, @@ -231,64 +220,54 @@ func TestCommandSide_SetCustomMessageText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageGreeting, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageSubject, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageTitle, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessagePreHeader, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageText, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageButtonText, - language.English, - ), - ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - domain.MessageFooterText, - language.English, - ), - ), - }, + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageGreeting, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageSubject, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageTitle, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessagePreHeader, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageText, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageButtonText, + language.English, + ), + + org.NewCustomTextRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + domain.MessageFooterText, + language.English, + ), ), ), }, @@ -472,15 +451,11 @@ func TestCommandSide_RemoveCustomMessageText(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewCustomTextTemplateRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "Template", - language.English, - ), - ), - }, + org.NewCustomTextTemplateRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "Template", + language.English, + ), ), ), }, diff --git a/internal/command/org_domain_test.go b/internal/command/org_domain_test.go index feb737cde..ace9cb874 100644 --- a/internal/command/org_domain_test.go +++ b/internal/command/org_domain_test.go @@ -15,7 +15,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -366,12 +365,10 @@ func TestCommandSide_AddOrgDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - }, + org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), ), ), }, @@ -572,19 +569,17 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerificationAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - domain.OrgDomainValidationTypeDNS, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - )), - }, + org.NewDomainVerificationAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + domain.OrgDomainValidationTypeDNS, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), ), ), secretGenerator: GetMockSecretGenerator(t), @@ -624,19 +619,17 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerificationAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - domain.OrgDomainValidationTypeHTTP, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - )), - }, + org.NewDomainVerificationAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + domain.OrgDomainValidationTypeHTTP, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), ), ), secretGenerator: GetMockSecretGenerator(t), @@ -875,12 +868,10 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerificationFailedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - }, + org.NewDomainVerificationFailedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -933,13 +924,10 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("domain.ch")), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -995,13 +983,10 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("domain.ch")), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1078,21 +1063,16 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) { &org.NewAggregate("org2").Aggregate, false, false, false))), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - eventFromEventPusher(user.NewDomainClaimedEvent(context.Background(), - &user.NewAggregate("user1", "org2").Aggregate, - "tempid@temporary.zitadel.ch", - "username@domain.ch", - false, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("domain.ch")), - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username@domain.ch", "org2", false)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("tempid@temporary.zitadel.ch", "org2", false)), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), + user.NewDomainClaimedEvent(context.Background(), + &user.NewAggregate("user1", "org2").Aggregate, + "tempid@temporary.zitadel.ch", + "username@domain.ch", + false, + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1283,12 +1263,10 @@ func TestCommandSide_SetPrimaryDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", - )), - }, + org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", + ), ), ), }, @@ -1476,12 +1454,10 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", false, - )), - }, + org.NewDomainRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", false, + ), ), ), }, @@ -1526,13 +1502,10 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "domain.ch", true, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("domain.ch")), + org.NewDomainRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "domain.ch", true, + ), ), ), }, diff --git a/internal/command/org_flow_test.go b/internal/command/org_flow_test.go index 5a856015b..178cfd44a 100644 --- a/internal/command/org_flow_test.go +++ b/internal/command/org_flow_test.go @@ -79,11 +79,9 @@ func TestCommands_ClearFlow(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - org.NewFlowClearedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.FlowTypeExternalAuthentication, - ), + org.NewFlowClearedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.FlowTypeExternalAuthentication, ), ), ), @@ -241,13 +239,11 @@ func TestCommands_SetTriggerActions(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - org.NewTriggerActionsSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.FlowTypeExternalAuthentication, - domain.TriggerTypePostAuthentication, - []string{"actionID1"}, - ), + org.NewTriggerActionsSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.FlowTypeExternalAuthentication, + domain.TriggerTypePostAuthentication, + []string{"actionID1"}, ), ), ), diff --git a/internal/command/org_idp_config_test.go b/internal/command/org_idp_config_test.go index 1705a620b..cffb32433 100644 --- a/internal/command/org_idp_config_test.go +++ b/internal/command/org_idp_config_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -91,38 +90,31 @@ func TestCommandSide_AddIDPConfig(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIDPConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1", - "name1", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeGoogle, - true, - ), - ), - eventFromEventPusher( - org.NewIDPOIDCConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "clientid1", - "config1", - "issuer", - "authorization-endpoint", - "token-endpoint", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("secret"), - }, - domain.OIDCMappingFieldEmail, - domain.OIDCMappingFieldEmail, - "scope", - ), - ), - }, - uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "org1")), + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "name1", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeGoogle, + true, + ), + org.NewIDPOIDCConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "clientid1", + "config1", + "issuer", + "authorization-endpoint", + "token-endpoint", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("secret"), + }, + domain.OIDCMappingFieldEmail, + domain.OIDCMappingFieldEmail, + "scope", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"), @@ -167,29 +159,22 @@ func TestCommandSide_AddIDPConfig(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIDPConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1", - "name1", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeGoogle, - false, - ), - ), - eventFromEventPusher( - org.NewIDPJWTConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1", - "jwt-endpoint", - "issuer", - "keys-endpoint", - "auth", - ), - ), - }, - uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name1", "org1")), + org.NewIDPConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "name1", + domain.IDPConfigTypeOIDC, + domain.IDPConfigStylingTypeGoogle, + false, + ), + org.NewIDPJWTConfigAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + "jwt-endpoint", + "issuer", + "keys-endpoint", + "auth", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "config1"), @@ -350,13 +335,7 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newIDPConfigChangedEvent(context.Background(), "org1", "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified), - ), - }, - uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "org1")), - uniqueConstraintsFromEventConstraint(idpconfig.NewAddIDPConfigNameUniqueConstraint("name2", "org1")), + newIDPConfigChangedEvent(context.Background(), "org1", "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified), ), ), }, @@ -474,14 +453,11 @@ func TestCommands_RemoveIDPConfig(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - org.NewIDPConfigRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idp1", - "name1", - ), + org.NewIDPConfigRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idp1", + "name1", ), - uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "org1")), ), ), }, @@ -540,24 +516,20 @@ func TestCommands_RemoveIDPConfig(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - org.NewIDPConfigRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idp1", - "name1", - ), - org.NewIdentityProviderCascadeRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idp1", - ), - user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "idp1", - "id1", - ), + org.NewIDPConfigRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idp1", + "name1", + ), + org.NewIdentityProviderCascadeRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "idp1", + ), + user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idp1", + "id1", ), - uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "org1")), - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("idp1", "id1")), ), ), }, diff --git a/internal/command/org_idp_jwt_config_test.go b/internal/command/org_idp_jwt_config_test.go index 4923b8180..1706f27cc 100644 --- a/internal/command/org_idp_jwt_config_test.go +++ b/internal/command/org_idp_jwt_config_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/org" @@ -211,18 +210,14 @@ func TestCommandSide_ChangeIDPJWTConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newIDPJWTConfigChangedEvent(context.Background(), - "org1", - "config1", - "jwt-endpoint-changed", - "issuer-changed", - "keys-endpoint-changed", - "auth-changed", - ), - ), - }, + newIDPJWTConfigChangedEvent(context.Background(), + "org1", + "config1", + "jwt-endpoint-changed", + "issuer-changed", + "keys-endpoint-changed", + "auth-changed", + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/org_idp_oidc_config_test.go b/internal/command/org_idp_oidc_config_test.go index 2fd91d701..0122e6fed 100644 --- a/internal/command/org_idp_oidc_config_test.go +++ b/internal/command/org_idp_oidc_config_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/org" @@ -241,27 +240,23 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newIDPOIDCConfigChangedEvent(context.Background(), - "org1", - "config1", - "clientid-changed", - "issuer-changed", - "authorization-endpoint-changed", - "token-endpoint-changed", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("secret-changed"), - }, - domain.OIDCMappingFieldPreferredLoginName, - domain.OIDCMappingFieldPreferredLoginName, - []string{"scope", "scope2"}, - ), - ), - }, + newIDPOIDCConfigChangedEvent(context.Background(), + "org1", + "config1", + "clientid-changed", + "issuer-changed", + "authorization-endpoint-changed", + "token-endpoint-changed", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("secret-changed"), + }, + domain.OIDCMappingFieldPreferredLoginName, + domain.OIDCMappingFieldPreferredLoginName, + []string{"scope", "scope2"}, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index f85fb1c21..d6eef4a39 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -14,7 +14,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/idp" @@ -195,24 +194,23 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewOAuthIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - "idAttribute", - nil, - idp.Options{}, - )), + org.NewOAuthIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -242,29 +240,28 @@ func TestCommandSide_AddOrgGenericOAuthIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewOAuthIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - "idAttribute", - []string{"user"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewOAuthIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -569,36 +566,34 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewOAuthIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.OAuthIDPChanges{ - idp.ChangeOAuthName("new name"), - idp.ChangeOAuthClientID("clientID2"), - idp.ChangeOAuthClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeOAuthAuthorizationEndpoint("new auth"), - idp.ChangeOAuthTokenEndpoint("new token"), - idp.ChangeOAuthUserEndpoint("new user"), - idp.ChangeOAuthScopes([]string{"openid", "profile"}), - idp.ChangeOAuthIDAttribute("newAttribute"), - idp.ChangeOAuthOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewOAuthIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.OAuthIDPChanges{ + idp.ChangeOAuthName("new name"), + idp.ChangeOAuthClientID("clientID2"), + idp.ChangeOAuthClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeOAuthAuthorizationEndpoint("new auth"), + idp.ChangeOAuthTokenEndpoint("new token"), + idp.ChangeOAuthUserEndpoint("new user"), + idp.ChangeOAuthScopes([]string{"openid", "profile"}), + idp.ChangeOAuthIDAttribute("newAttribute"), + idp.ChangeOAuthOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -754,22 +749,21 @@ func TestCommandSide_AddOrgGenericOIDCIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - false, - idp.Options{}, - )), + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -796,27 +790,26 @@ func TestCommandSide_AddOrgGenericOIDCIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{openid.ScopeOpenID}, - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewOIDCIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{openid.ScopeOpenID}, + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1043,34 +1036,32 @@ func TestCommandSide_UpdateOrgGenericOIDCIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewOIDCIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.OIDCIDPChanges{ - idp.ChangeOIDCName("new name"), - idp.ChangeOIDCIssuer("new issuer"), - idp.ChangeOIDCClientID("clientID2"), - idp.ChangeOIDCClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeOIDCScopes([]string{"openid", "profile"}), - idp.ChangeOIDCIsIDTokenMapping(true), - idp.ChangeOIDCOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewOIDCIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.OIDCIDPChanges{ + idp.ChangeOIDCName("new name"), + idp.ChangeOIDCIssuer("new issuer"), + idp.ChangeOIDCClientID("clientID2"), + idp.ChangeOIDCClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeOIDCScopes([]string{"openid", "profile"}), + idp.ChangeOIDCIsIDTokenMapping(true), + idp.ChangeOIDCOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1237,28 +1228,24 @@ func TestCommandSide_MigrateOrgGenericOIDCToAzureADProvider(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - func() eventstore.Command { - event := org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - "", - false, - idp.Options{}, - ) - return event - }(), - ), - }, + func() eventstore.Command { + event := org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1300,27 +1287,26 @@ func TestCommandSide_MigrateOrgGenericOIDCToAzureADProvider(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - "tenant", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewOIDCIDPMigratedAzureADEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1469,20 +1455,19 @@ func TestCommandSide_MigrateOrgOIDCToGoogleIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), + org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1523,25 +1508,24 @@ func TestCommandSide_MigrateOrgOIDCToGoogleIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewOIDCIDPMigratedGoogleEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1671,22 +1655,21 @@ func TestCommandSide_AddOrgAzureADIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - "", - false, - idp.Options{}, - )), + org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + "", + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1712,27 +1695,26 @@ func TestCommandSide_AddOrgAzureADIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - "tenant", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + "tenant", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -1937,34 +1919,32 @@ func TestCommandSide_UpdateOrgAzureADIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewAzureADIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.AzureADIDPChanges{ - idp.ChangeAzureADName("new name"), - idp.ChangeAzureADClientID("new clientID"), - idp.ChangeAzureADClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new clientSecret"), - }), - idp.ChangeAzureADScopes([]string{"openid", "profile"}), - idp.ChangeAzureADTenant("new tenant"), - idp.ChangeAzureADIsEmailVerified(true), - idp.ChangeAzureADOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewAzureADIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.AzureADIDPChanges{ + idp.ChangeAzureADName("new name"), + idp.ChangeAzureADClientID("new clientID"), + idp.ChangeAzureADClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeAzureADScopes([]string{"openid", "profile"}), + idp.ChangeAzureADTenant("new tenant"), + idp.ChangeAzureADIsEmailVerified(true), + idp.ChangeAzureADOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2077,20 +2057,19 @@ func TestCommandSide_AddOrgGitHubIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2115,25 +2094,24 @@ func TestCommandSide_AddOrgGitHubIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewGitHubIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2311,32 +2289,30 @@ func TestCommandSide_UpdateOrgGitHubIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewGitHubIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.GitHubIDPChanges{ - idp.ChangeGitHubName("new name"), - idp.ChangeGitHubClientID("new clientID"), - idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new clientSecret"), - }), - idp.ChangeGitHubScopes([]string{"openid", "profile"}), - idp.ChangeGitHubOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewGitHubIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitHubIDPChanges{ + idp.ChangeGitHubName("new name"), + idp.ChangeGitHubClientID("new clientID"), + idp.ChangeGitHubClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new clientSecret"), + }), + idp.ChangeGitHubScopes([]string{"openid", "profile"}), + idp.ChangeGitHubOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2533,23 +2509,22 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - nil, - idp.Options{}, - )), + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2578,28 +2553,27 @@ func TestCommandSide_AddOrgGitHubEnterpriseIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - "auth", - "token", - "user", - []string{"user"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewGitHubEnterpriseIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + []string{"user"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -2876,35 +2850,33 @@ func TestCommandSide_UpdateOrgGitHubEnterpriseIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.GitHubEnterpriseIDPChanges{ - idp.ChangeGitHubEnterpriseName("new name"), - idp.ChangeGitHubEnterpriseClientID("clientID2"), - idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), - idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), - idp.ChangeGitHubEnterpriseUserEndpoint("new user"), - idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), - idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewGitHubEnterpriseIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitHubEnterpriseIDPChanges{ + idp.ChangeGitHubEnterpriseName("new name"), + idp.ChangeGitHubEnterpriseClientID("clientID2"), + idp.ChangeGitHubEnterpriseClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitHubEnterpriseAuthorizationEndpoint("new auth"), + idp.ChangeGitHubEnterpriseTokenEndpoint("new token"), + idp.ChangeGitHubEnterpriseUserEndpoint("new user"), + idp.ChangeGitHubEnterpriseScopes([]string{"openid", "profile"}), + idp.ChangeGitHubEnterpriseOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -3018,22 +2990,19 @@ func TestCommandSide_AddOrgGitLabIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewGitLabIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + org.NewGitLabIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3058,27 +3027,24 @@ func TestCommandSide_AddOrgGitLabIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewGitLabIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + org.NewGitLabIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3255,33 +3221,29 @@ func TestCommandSide_UpdateOrgGitLabIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - func() eventstore.Command { - t := true - event, _ := org.NewGitLabIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.GitLabIDPChanges{ - idp.ChangeGitLabClientID("clientID2"), - idp.ChangeGitLabClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitLabScopes([]string{"openid", "profile"}), - idp.ChangeGitLabOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := org.NewGitLabIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitLabIDPChanges{ + idp.ChangeGitLabClientID("clientID2"), + idp.ChangeGitLabClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitLabScopes([]string{"openid", "profile"}), + idp.ChangeGitLabOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -3432,23 +3394,20 @@ func TestCommandSide_AddOrgGitLabSelfHostedIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), - }, + org.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3475,28 +3434,25 @@ func TestCommandSide_AddOrgGitLabSelfHostedIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - "issuer", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), - }, + org.NewGitLabSelfHostedIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + "issuer", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3720,35 +3676,31 @@ func TestCommandSide_UpdateOrgGitLabSelfHostedIDP(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - func() eventstore.Command { - t := true - event, _ := org.NewGitLabSelfHostedIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.GitLabSelfHostedIDPChanges{ - idp.ChangeGitLabSelfHostedClientID("clientID2"), - idp.ChangeGitLabSelfHostedIssuer("newIssuer"), - idp.ChangeGitLabSelfHostedName("newName"), - idp.ChangeGitLabSelfHostedClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGitLabSelfHostedScopes([]string{"openid", "profile"}), - idp.ChangeGitLabSelfHostedOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), - }, + func() eventstore.Command { + t := true + event, _ := org.NewGitLabSelfHostedIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GitLabSelfHostedIDPChanges{ + idp.ChangeGitLabSelfHostedClientID("clientID2"), + idp.ChangeGitLabSelfHostedIssuer("newIssuer"), + idp.ChangeGitLabSelfHostedName("newName"), + idp.ChangeGitLabSelfHostedClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGitLabSelfHostedScopes([]string{"openid", "profile"}), + idp.ChangeGitLabSelfHostedOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -3860,20 +3812,19 @@ func TestCommandSide_AddOrgGoogleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGoogleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - nil, - idp.Options{}, - )), + org.NewGoogleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -3898,25 +3849,24 @@ func TestCommandSide_AddOrgGoogleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewGoogleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("clientSecret"), - }, - []string{"openid"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewGoogleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + []string{"openid"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4093,31 +4043,29 @@ func TestCommandSide_UpdateOrgGoogleIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewGoogleIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.GoogleIDPChanges{ - idp.ChangeGoogleClientID("clientID2"), - idp.ChangeGoogleClientSecret(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newSecret"), - }), - idp.ChangeGoogleScopes([]string{"openid", "profile"}), - idp.ChangeGoogleOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewGoogleIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.GoogleIDPChanges{ + idp.ChangeGoogleClientID("clientID2"), + idp.ChangeGoogleClientSecret(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newSecret"), + }), + idp.ChangeGoogleScopes([]string{"openid", "profile"}), + idp.ChangeGoogleOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -4362,27 +4310,26 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewLDAPIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - []string{"server"}, - false, - "baseDN", - "dn", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - "user", - []string{"object"}, - []string{"filter"}, - time.Second*30, - idp.LDAPAttributes{}, - idp.Options{}, - )), + org.NewLDAPIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + []string{"server"}, + false, + "baseDN", + "dn", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + "user", + []string{"object"}, + []string{"filter"}, + time.Second*30, + idp.LDAPAttributes{}, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4415,46 +4362,45 @@ func TestCommandSide_AddOrgLDAPIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewLDAPIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - []string{"server"}, - false, - "baseDN", - "dn", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - "user", - []string{"object"}, - []string{"filter"}, - time.Second*30, - idp.LDAPAttributes{ - IDAttribute: "id", - FirstNameAttribute: "firstName", - LastNameAttribute: "lastName", - DisplayNameAttribute: "displayName", - NickNameAttribute: "nickName", - PreferredUsernameAttribute: "preferredUsername", - EmailAttribute: "email", - EmailVerifiedAttribute: "emailVerified", - PhoneAttribute: "phone", - PhoneVerifiedAttribute: "phoneVerified", - PreferredLanguageAttribute: "preferredLanguage", - AvatarURLAttribute: "avatarURL", - ProfileAttribute: "profile", - }, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewLDAPIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + []string{"server"}, + false, + "baseDN", + "dn", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + "user", + []string{"object"}, + []string{"filter"}, + time.Second*30, + idp.LDAPAttributes{ + IDAttribute: "id", + FirstNameAttribute: "firstName", + LastNameAttribute: "lastName", + DisplayNameAttribute: "displayName", + NickNameAttribute: "nickName", + PreferredUsernameAttribute: "preferredUsername", + EmailAttribute: "email", + EmailVerifiedAttribute: "emailVerified", + PhoneAttribute: "phone", + PhoneVerifiedAttribute: "phoneVerified", + PreferredLanguageAttribute: "preferredLanguage", + AvatarURLAttribute: "avatarURL", + ProfileAttribute: "profile", + }, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4812,53 +4758,51 @@ func TestCommandSide_UpdateOrgLDAPIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewLDAPIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.LDAPIDPChanges{ - idp.ChangeLDAPName("new name"), - idp.ChangeLDAPServers([]string{"new server"}), - idp.ChangeLDAPStartTLS(true), - idp.ChangeLDAPBaseDN("new basedn"), - idp.ChangeLDAPBindDN("new binddn"), - idp.ChangeLDAPBindPassword(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new password"), - }), - idp.ChangeLDAPUserBase("new user"), - idp.ChangeLDAPUserObjectClasses([]string{"new object"}), - idp.ChangeLDAPUserFilters([]string{"new filter"}), - idp.ChangeLDAPTimeout(time.Second * 20), - idp.ChangeLDAPAttributes(idp.LDAPAttributeChanges{ - IDAttribute: stringPointer("new id"), - FirstNameAttribute: stringPointer("new firstName"), - LastNameAttribute: stringPointer("new lastName"), - DisplayNameAttribute: stringPointer("new displayName"), - NickNameAttribute: stringPointer("new nickName"), - PreferredUsernameAttribute: stringPointer("new preferredUsername"), - EmailAttribute: stringPointer("new email"), - EmailVerifiedAttribute: stringPointer("new emailVerified"), - PhoneAttribute: stringPointer("new phone"), - PhoneVerifiedAttribute: stringPointer("new phoneVerified"), - PreferredLanguageAttribute: stringPointer("new preferredLanguage"), - AvatarURLAttribute: stringPointer("new avatarURL"), - ProfileAttribute: stringPointer("new profile"), - }), - idp.ChangeLDAPOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewLDAPIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.LDAPIDPChanges{ + idp.ChangeLDAPName("new name"), + idp.ChangeLDAPServers([]string{"new server"}), + idp.ChangeLDAPStartTLS(true), + idp.ChangeLDAPBaseDN("new basedn"), + idp.ChangeLDAPBindDN("new binddn"), + idp.ChangeLDAPBindPassword(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new password"), + }), + idp.ChangeLDAPUserBase("new user"), + idp.ChangeLDAPUserObjectClasses([]string{"new object"}), + idp.ChangeLDAPUserFilters([]string{"new filter"}), + idp.ChangeLDAPTimeout(time.Second * 20), + idp.ChangeLDAPAttributes(idp.LDAPAttributeChanges{ + IDAttribute: stringPointer("new id"), + FirstNameAttribute: stringPointer("new firstName"), + LastNameAttribute: stringPointer("new lastName"), + DisplayNameAttribute: stringPointer("new displayName"), + NickNameAttribute: stringPointer("new nickName"), + PreferredUsernameAttribute: stringPointer("new preferredUsername"), + EmailAttribute: stringPointer("new email"), + EmailVerifiedAttribute: stringPointer("new emailVerified"), + PhoneAttribute: stringPointer("new phone"), + PhoneVerifiedAttribute: stringPointer("new phoneVerified"), + PreferredLanguageAttribute: stringPointer("new preferredLanguage"), + AvatarURLAttribute: stringPointer("new avatarURL"), + ProfileAttribute: stringPointer("new profile"), + }), + idp.ChangeLDAPOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -5031,22 +4975,21 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewAppleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - "teamID", - "keyID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("privateKey"), - }, - nil, - idp.Options{}, - )), + org.NewAppleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + "teamID", + "keyID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("privateKey"), + }, + nil, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5073,27 +5016,26 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewAppleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "", - "clientID", - "teamID", - "keyID", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("privateKey"), - }, - []string{"name", "email"}, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewAppleIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "", + "clientID", + "teamID", + "keyID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("privateKey"), + }, + []string{"name", "email"}, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5319,33 +5261,31 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewAppleIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.AppleIDPChanges{ - idp.ChangeAppleClientID("clientID2"), - idp.ChangeAppleTeamID("teamID2"), - idp.ChangeAppleKeyID("keyID2"), - idp.ChangeApplePrivateKey(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("newPrivateKey"), - }), - idp.ChangeAppleScopes([]string{"name", "email"}), - idp.ChangeAppleOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewAppleIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.AppleIDPChanges{ + idp.ChangeAppleClientID("clientID2"), + idp.ChangeAppleTeamID("teamID2"), + idp.ChangeAppleKeyID("keyID2"), + idp.ChangeApplePrivateKey(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("newPrivateKey"), + }), + idp.ChangeAppleScopes([]string{"name", "email"}), + idp.ChangeAppleOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -5462,22 +5402,21 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - []byte("metadata"), - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("key"), - }, - []byte("certificate"), - "", - false, - idp.Options{}, - )), + org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + []byte("metadata"), + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("key"), + }, + []byte("certificate"), + "", + false, + idp.Options{}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5502,27 +5441,26 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter(), expectPush( - eventPusherToEvents( - org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - "name", - []byte("metadata"), - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("key"), - }, - []byte("certificate"), - "binding", - true, - idp.Options{ - IsCreationAllowed: true, - IsLinkingAllowed: true, - IsAutoCreation: true, - IsAutoUpdate: true, - }, - )), + org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + "name", + []byte("metadata"), + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("key"), + }, + []byte("certificate"), + "binding", + true, + idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5729,27 +5667,25 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - t := true - event, _ := org.NewSAMLIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.SAMLIDPChanges{ - idp.ChangeSAMLName("new name"), - idp.ChangeSAMLMetadata([]byte("new metadata")), - idp.ChangeSAMLBinding("new binding"), - idp.ChangeSAMLWithSignedRequest(true), - idp.ChangeSAMLOptions(idp.OptionChanges{ - IsCreationAllowed: &t, - IsLinkingAllowed: &t, - IsAutoCreation: &t, - IsAutoUpdate: &t, - }), - }, - ) - return event - }(), - ), + func() eventstore.Command { + t := true + event, _ := org.NewSAMLIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.SAMLIDPChanges{ + idp.ChangeSAMLName("new name"), + idp.ChangeSAMLMetadata([]byte("new metadata")), + idp.ChangeSAMLBinding("new binding"), + idp.ChangeSAMLWithSignedRequest(true), + idp.ChangeSAMLOptions(idp.OptionChanges{ + IsCreationAllowed: &t, + IsLinkingAllowed: &t, + IsAutoCreation: &t, + IsAutoUpdate: &t, + }), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -5873,23 +5809,21 @@ func TestCommandSide_RegenerateOrgSAMLProviderCertificate(t *testing.T) { )), ), expectPush( - eventPusherToEvents( - func() eventstore.Command { - event, _ := org.NewSAMLIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "id1", - []idp.SAMLIDPChanges{ - idp.ChangeSAMLKey(&crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("new key"), - }), - idp.ChangeSAMLCertificate([]byte("new certificate")), - }, - ) - return event - }(), - ), + func() eventstore.Command { + event, _ := org.NewSAMLIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, + "id1", + []idp.SAMLIDPChanges{ + idp.ChangeSAMLKey(&crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("new key"), + }), + idp.ChangeSAMLCertificate([]byte("new certificate")), + }, + ) + return event + }(), ), ), secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/org_member_test.go b/internal/command/org_member_test.go index 309c6046a..db2b9038a 100644 --- a/internal/command/org_member_test.go +++ b/internal/command/org_member_test.go @@ -12,9 +12,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" @@ -432,14 +430,11 @@ func TestCommandSide_AddOrgMember(t *testing.T) { ), expectFilter(), expectPushFailed(errors.ThrowAlreadyExists(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "user1", - []string{"ORG_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org1", "user1")), + org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "user1", + []string{"ORG_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -481,14 +476,11 @@ func TestCommandSide_AddOrgMember(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "user1", - []string{"ORG_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org1", "user1")), + org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "user1", + []string{"ORG_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -671,13 +663,11 @@ func TestCommandSide_ChangeOrgMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewMemberChangedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "user1", - []string{"ORG_OWNER", "ORG_OWNER_VIEWER"}..., - )), - }, + org.NewMemberChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "user1", + []string{"ORG_OWNER", "ORG_OWNER_VIEWER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -818,13 +808,10 @@ func TestCommandSide_RemoveOrgMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectMemberRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - )), - }, - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")), + project.NewProjectMemberRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + ), ), ), }, diff --git a/internal/command/org_metadata_test.go b/internal/command/org_metadata_test.go index 468e0dc05..2f20d12c6 100644 --- a/internal/command/org_metadata_test.go +++ b/internal/command/org_metadata_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -95,15 +94,11 @@ func TestCommandSide_SetOrgMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMetadataSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - []byte("value"), - ), - ), - }, + org.NewMetadataSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + []byte("value"), + ), ), ), }, @@ -244,22 +239,16 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMetadataSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - []byte("value"), - ), - ), - eventFromEventPusher( - org.NewMetadataSetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key1", - []byte("value1"), - ), - ), - }, + org.NewMetadataSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + []byte("value"), + ), + org.NewMetadataSetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key1", + []byte("value1"), + ), ), ), }, @@ -399,14 +388,10 @@ func TestCommandSide_OrgRemoveMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMetadataRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewMetadataRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, @@ -597,20 +582,14 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMetadataRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - eventFromEventPusher( - org.NewMetadataRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key1", - ), - ), - }, + org.NewMetadataRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), + org.NewMetadataRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key1", + ), ), ), }, diff --git a/internal/command/org_policy_domain_test.go b/internal/command/org_policy_domain_test.go index c034271c7..c431612a5 100644 --- a/internal/command/org_policy_domain_test.go +++ b/internal/command/org_policy_domain_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -100,16 +99,12 @@ func TestCommandSide_AddDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - }, + org.NewDomainPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, + ), ), ), }, @@ -202,38 +197,26 @@ func TestCommandSide_AddDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "user1@org.com", - "user1", - true, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user2", "org1").Aggregate, - "user@test.com", - "user@test.com", - true, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user1@org.com", "org1", false)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("user1", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user@test.com", "org1", false)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("user@test.com", "org1", true)), + org.NewDomainPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, + ), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "user1@org.com", + "user1", + true, + user.UsernameChangedEventWithPolicyChange(), + ), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user2", "org1").Aggregate, + "user@test.com", + "user@test.com", + true, + user.UsernameChangedEventWithPolicyChange(), + ), ), ), }, @@ -371,14 +354,10 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDomainPolicyChangedEvent(context.Background(), "org1", - policy.ChangeValidateOrgDomains(false), - policy.ChangeSMTPSenderAddressMatchesInstanceDomain(false), - ), - ), - }, + newDomainPolicyChangedEvent(context.Background(), "org1", + policy.ChangeValidateOrgDomains(false), + policy.ChangeSMTPSenderAddressMatchesInstanceDomain(false), + ), ), ), }, @@ -440,26 +419,18 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newDomainPolicyChangedEvent(context.Background(), "org1", - policy.ChangeUserLoginMustBeDomain(false), - policy.ChangeValidateOrgDomains(false), - policy.ChangeSMTPSenderAddressMatchesInstanceDomain(false), - ), - ), - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "user1", - "user1@org.com", - false, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user1", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("user1@org.com", "org1", false)), + newDomainPolicyChangedEvent(context.Background(), "org1", + policy.ChangeUserLoginMustBeDomain(false), + policy.ChangeValidateOrgDomains(false), + policy.ChangeSMTPSenderAddressMatchesInstanceDomain(false), + ), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "user1", + "user1@org.com", + false, + user.UsernameChangedEventWithPolicyChange(), + ), ), ), }, @@ -570,12 +541,8 @@ func TestCommandSide_RemoveDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewDomainPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewDomainPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, @@ -644,23 +611,16 @@ func TestCommandSide_RemoveDomainPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewDomainPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "user1", - "user1@org.com", - false, - user.UsernameChangedEventWithPolicyChange(), - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user1", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("user1@org.com", "org1", false)), + org.NewDomainPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + ), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "user1", + "user1@org.com", + false, + user.UsernameChangedEventWithPolicyChange(), + ), ), ), }, diff --git a/internal/command/org_policy_label_test.go b/internal/command/org_policy_label_test.go index c4fc41edf..d317b8cd8 100644 --- a/internal/command/org_policy_label_test.go +++ b/internal/command/org_policy_label_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -109,24 +108,20 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - "#ffffff", - true, - true, - true, - ), - ), - }, + org.NewLabelPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + true, + true, + true, + ), ), ), }, @@ -315,24 +310,20 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newLabelPolicyChangedEvent( - context.Background(), - "org1", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - "#000000", - false, - false, - false), - ), - }, + newLabelPolicyChangedEvent( + context.Background(), + "org1", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + false, + false, + false), ), ), }, @@ -464,12 +455,8 @@ func TestCommandSide_ActivateLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyActivatedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewLabelPolicyActivatedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, @@ -568,12 +555,8 @@ func TestCommandSide_RemoveLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewLabelPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectsNoError(), @@ -735,14 +718,10 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyLogoAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "logo", - ), - ), - }, + org.NewLabelPolicyLogoAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "logo", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -869,14 +848,10 @@ func TestCommandSide_RemoveLogoLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyLogoRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewLabelPolicyLogoRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, @@ -1045,14 +1020,10 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyIconAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "icon", - ), - ), - }, + org.NewLabelPolicyIconAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "icon", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1177,14 +1148,10 @@ func TestCommandSide_RemoveIconLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyIconRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewLabelPolicyIconRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, @@ -1352,14 +1319,10 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyLogoDarkAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "logo", - ), - ), - }, + org.NewLabelPolicyLogoDarkAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "logo", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1486,14 +1449,10 @@ func TestCommandSide_RemoveLogoDarkLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyLogoDarkRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewLabelPolicyLogoDarkRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, @@ -1662,14 +1621,10 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyIconDarkAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "icon", - ), - ), - }, + org.NewLabelPolicyIconDarkAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "icon", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -1792,14 +1747,10 @@ func TestCommandSide_RemoveIconDarkLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyIconDarkRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewLabelPolicyIconDarkRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, @@ -1959,14 +1910,10 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyFontAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "font", - ), - ), - }, + org.NewLabelPolicyFontAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "font", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -2089,14 +2036,10 @@ func TestCommandSide_RemoveFontLabelPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLabelPolicyFontRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "key", - ), - ), - }, + org.NewLabelPolicyFontRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "key", + ), ), ), }, diff --git a/internal/command/org_policy_lockout_test.go b/internal/command/org_policy_lockout_test.go index e33917963..0cb7c056d 100644 --- a/internal/command/org_policy_lockout_test.go +++ b/internal/command/org_policy_lockout_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -87,15 +86,11 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLockoutPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 10, - true, - ), - ), - }, + org.NewLockoutPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + 10, + true, + ), ), ), }, @@ -238,11 +233,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newPasswordLockoutPolicyChangedEvent(context.Background(), "org1", 5, false), - ), - }, + newPasswordLockoutPolicyChangedEvent(context.Background(), "org1", 5, false), ), ), }, @@ -348,12 +339,8 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLockoutPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewLockoutPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_policy_login_test.go b/internal/command/org_policy_login_test.go index 54d48062d..c04e5a709 100644 --- a/internal/command/org_policy_login_test.go +++ b/internal/command/org_policy_login_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -107,30 +106,26 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - domain.PasswordlessTypeAllowed, - "https://example.com/redirect", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - }, + org.NewLoginPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + domain.PasswordlessTypeAllowed, + "https://example.com/redirect", + time.Hour*1, + time.Hour*2, + time.Hour*3, + time.Hour*4, + time.Hour*5, + ), ), ), }, @@ -205,42 +200,34 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - domain.PasswordlessTypeAllowed, - "https://example.com/redirect", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - eventFromEventPusher( - org.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeTOTP, - ), - ), - eventFromEventPusher( - org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.MultiFactorTypeU2FWithPIN, - ), - ), - }, + org.NewLoginPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + domain.PasswordlessTypeAllowed, + "https://example.com/redirect", + time.Hour*1, + time.Hour*2, + time.Hour*3, + time.Hour*4, + time.Hour*5, + ), + org.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeTOTP, + ), + org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.MultiFactorTypeU2FWithPIN, + ), ), ), }, @@ -336,37 +323,31 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - domain.PasswordlessTypeAllowed, - "https://example.com/redirect", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - eventFromEventPusher( - org.NewIdentityProviderAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1", - domain.IdentityProviderTypeSystem, - ), - ), - }, + org.NewLoginPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + domain.PasswordlessTypeAllowed, + "https://example.com/redirect", + time.Hour*1, + time.Hour*2, + time.Hour*3, + time.Hour*4, + time.Hour*5, + ), + org.NewIdentityProviderAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + domain.IdentityProviderTypeSystem, + ), ), ), }, @@ -561,30 +542,26 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newLoginPolicyChangedEvent(context.Background(), - "org1", - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - &duration10, - &duration20, - &duration30, - &duration40, - &duration50, - ), - ), - }, + newLoginPolicyChangedEvent(context.Background(), + "org1", + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + domain.PasswordlessTypeNotAllowed, + "", + &duration10, + &duration20, + &duration30, + &duration40, + &duration50, + ), ), ), }, @@ -714,12 +691,8 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewLoginPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, @@ -981,14 +954,10 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIdentityProviderAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1", - domain.IdentityProviderTypeOrg), - ), - }, + org.NewIdentityProviderAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + domain.IdentityProviderTypeOrg), ), ), }, @@ -1247,13 +1216,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIdentityProviderRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1"), - ), - }, + org.NewIdentityProviderRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1"), ), ), }, @@ -1311,13 +1276,9 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIdentityProviderRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1"), - ), - }, + org.NewIdentityProviderRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1"), ), ), }, @@ -1390,18 +1351,14 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewIdentityProviderRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "config1"), - ), - eventFromEventPusher( - user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "config1", "externaluser1")), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("config1", "externaluser1")), + org.NewIdentityProviderRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "config1", + ), + user.NewUserIDPLinkCascadeRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "config1", "externaluser1", + ), ), ), }, @@ -1527,13 +1484,9 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeTOTP), - ), - }, + org.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeTOTP), ), ), }, @@ -1553,13 +1506,9 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeOTPEmail), - ), - }, + org.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeOTPEmail), ), ), }, @@ -1579,13 +1528,9 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + org.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -1612,13 +1557,9 @@ func TestCommandSide_AddSecondFactorLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + org.NewLoginPolicySecondFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -1821,13 +1762,9 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeTOTP), - ), - }, + org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeTOTP), ), ), }, @@ -1856,13 +1793,9 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeOTPEmail), - ), - }, + org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeOTPEmail), ), ), }, @@ -1891,13 +1824,9 @@ func TestCommandSide_RemoveSecondFactoroginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.SecondFactorTypeOTPSMS), - ), - }, + org.NewLoginPolicySecondFactorRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.SecondFactorTypeOTPSMS), ), ), }, @@ -2009,13 +1938,9 @@ func TestCommandSide_AddMultiFactorLoginPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.MultiFactorTypeU2FWithPIN), - ), - }, + org.NewLoginPolicyMultiFactorAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.MultiFactorTypeU2FWithPIN), ), ), }, @@ -2158,13 +2083,9 @@ func TestCommandSide_RemoveMultiFactorLoginPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewLoginPolicyMultiFactorRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - domain.MultiFactorTypeU2FWithPIN), - ), - }, + org.NewLoginPolicyMultiFactorRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + domain.MultiFactorTypeU2FWithPIN), ), ), }, diff --git a/internal/command/org_policy_mail_template_test.go b/internal/command/org_policy_mail_template_test.go index f2be80df6..ab4d1b1b9 100644 --- a/internal/command/org_policy_mail_template_test.go +++ b/internal/command/org_policy_mail_template_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -84,14 +83,10 @@ func TestCommandSide_AddMailTemplate(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMailTemplateAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - []byte("template"), - ), - ), - }, + org.NewMailTemplateAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + []byte("template"), + ), ), ), }, @@ -227,11 +222,7 @@ func TestCommandSide_ChangeMailTemplate(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newMailTemplateChangedEvent(context.Background(), "org1", "template2"), - ), - }, + newMailTemplateChangedEvent(context.Background(), "org1", "template2"), ), ), }, @@ -334,12 +325,8 @@ func TestCommandSide_RemoveMailTemplate(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewMailTemplateRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewMailTemplateRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_policy_notification_test.go b/internal/command/org_policy_notification_test.go index 7b568ebfd..5b780b892 100644 --- a/internal/command/org_policy_notification_test.go +++ b/internal/command/org_policy_notification_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -80,14 +79,10 @@ func TestCommandSide_AddNotificationPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewNotificationPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - ), - ), - }, + org.NewNotificationPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + ), ), ), }, @@ -109,14 +104,10 @@ func TestCommandSide_AddNotificationPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewNotificationPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - ), - ), - }, + org.NewNotificationPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + false, + ), ), ), }, @@ -240,11 +231,7 @@ func TestCommandSide_ChangeNotificationPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newNotificationPolicyChangedEvent(context.Background(), "org1", false), - ), - }, + newNotificationPolicyChangedEvent(context.Background(), "org1", false), ), ), }, @@ -341,12 +328,8 @@ func TestCommandSide_RemoveNotificationPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewNotificationPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewNotificationPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_policy_password_age_test.go b/internal/command/org_policy_password_age_test.go index 17f73e228..79b703a99 100644 --- a/internal/command/org_policy_password_age_test.go +++ b/internal/command/org_policy_password_age_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -87,15 +86,11 @@ func TestCommandSide_AddPasswordAgePolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPasswordAgePolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 10, - 365, - ), - ), - }, + org.NewPasswordAgePolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + 10, + 365, + ), ), ), }, @@ -238,11 +233,7 @@ func TestCommandSide_ChangePasswordAgePolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newPasswordAgePolicyChangedEvent(context.Background(), "org1", 150, 5), - ), - }, + newPasswordAgePolicyChangedEvent(context.Background(), "org1", 150, 5), ), ), }, @@ -348,12 +339,8 @@ func TestCommandSide_RemovePasswordAgePolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPasswordAgePolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewPasswordAgePolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_policy_password_complexity_test.go b/internal/command/org_policy_password_complexity_test.go index 44de9259a..93b73170e 100644 --- a/internal/command/org_policy_password_complexity_test.go +++ b/internal/command/org_policy_password_complexity_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -93,15 +92,11 @@ func TestCommandSide_AddPasswordComplexityPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 8, - true, true, true, true, - ), - ), - }, + org.NewPasswordComplexityPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + 8, + true, true, true, true, + ), ), ), }, @@ -259,11 +254,7 @@ func TestCommandSide_ChangePasswordComplexityPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newPasswordComplexityPolicyChangedEvent(context.Background(), "org1", 10, false, false, false, false), - ), - }, + newPasswordComplexityPolicyChangedEvent(context.Background(), "org1", 10, false, false, false, false), ), ), }, @@ -375,12 +366,8 @@ func TestCommandSide_RemovePasswordComplexityPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewPasswordComplexityPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_policy_privacy_test.go b/internal/command/org_policy_privacy_test.go index 986e7a021..bc5e44885 100644 --- a/internal/command/org_policy_privacy_test.go +++ b/internal/command/org_policy_privacy_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -93,17 +92,13 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPrivacyPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "TOSLink", - "PrivacyLink", - "HelpLink", - "support@example.com", - ), - ), - }, + org.NewPrivacyPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "TOSLink", + "PrivacyLink", + "HelpLink", + "support@example.com", + ), ), ), }, @@ -158,17 +153,13 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPrivacyPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "", - "", - "", - "", - ), - ), - }, + org.NewPrivacyPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "", + "", + "", + "", + ), ), ), }, @@ -341,11 +332,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange", "support2@example.com"), - ), - }, + newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange", "support2@example.com"), ), ), }, @@ -389,11 +376,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", "", ""), - ), - }, + newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", "", ""), ), ), }, @@ -505,12 +488,8 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewPrivacyPolicyRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate), - ), - }, + org.NewPrivacyPolicyRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate), ), ), }, diff --git a/internal/command/org_test.go b/internal/command/org_test.go index 8696b3fb2..6db8654d9 100644 --- a/internal/command/org_test.go +++ b/internal/command/org_test.go @@ -15,11 +15,9 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" - "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" @@ -196,31 +194,31 @@ func TestCommandSide_AddOrg(t *testing.T) { ), expectFilterOrgMemberNotFound(), expectPushFailed(errors.ThrowAlreadyExists(nil, "id", "internal"), - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "Org")), - eventFromEventPusher(org.NewDomainAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewDomainVerifiedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewDomainPrimarySetEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewMemberAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "user1", domain.RoleOrgOwner)), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")), + org.NewOrgAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "Org", + ), + org.NewDomainAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainVerifiedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainPrimarySetEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewMemberAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "user1", domain.RoleOrgOwner, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), @@ -264,31 +262,31 @@ func TestCommandSide_AddOrg(t *testing.T) { ), expectFilterOrgMemberNotFound(), expectPushFailed(errors.ThrowInternal(nil, "id", "internal"), - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "Org")), - eventFromEventPusher(org.NewDomainAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewDomainVerifiedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewDomainPrimarySetEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain")), - eventFromEventPusher(org.NewMemberAddedEvent( - context.Background(), - &org.NewAggregate("org2").Aggregate, - "user1", domain.RoleOrgOwner)), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")), + org.NewOrgAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "Org", + ), + org.NewDomainAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainVerifiedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainPrimarySetEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewMemberAddedEvent( + context.Background(), + &org.NewAggregate("org2").Aggregate, + "user1", domain.RoleOrgOwner, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), @@ -332,31 +330,26 @@ func TestCommandSide_AddOrg(t *testing.T) { ), expectFilterOrgMemberNotFound(), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "Org", - )), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "user1", - domain.RoleOrgOwner, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")), + org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "Org", + ), + org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, "org.iam-domain", + ), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "user1", + domain.RoleOrgOwner, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), @@ -408,31 +401,26 @@ func TestCommandSide_AddOrg(t *testing.T) { ), expectFilterOrgMemberNotFound(), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "Org", - )), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "user1", - domain.RoleOrgOwner, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("org2", "user1")), + org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "Org", + ), + org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, "org.iam-domain", + ), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "org.iam-domain", + ), + org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("org2").Aggregate, + "user1", + domain.RoleOrgOwner, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), @@ -586,12 +574,9 @@ func TestCommandSide_ChangeOrg(t *testing.T) { expectFilter(), expectPushFailed( errors.ThrowInternal(nil, "id", "message"), - []*repository.Event{ - eventFromEventPusher(org.NewOrgChangedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "org", "neworg")), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")), + org.NewOrgChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "org", "neworg", + ), ), ), }, @@ -634,20 +619,18 @@ func TestCommandSide_ChangeOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgChangedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "org", "neworg")), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch")), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch")), - eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "org.zitadel.ch", true)), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("neworg.zitadel.ch")), - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("org.zitadel.ch")), + org.NewOrgChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "org", "neworg", + ), + org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch", + ), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch", + ), + org.NewDomainRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "org.zitadel.ch", true, + ), ), ), }, @@ -693,22 +676,21 @@ func TestCommandSide_ChangeOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgChangedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "org", "neworg")), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch")), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch")), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch")), - eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, "org.zitadel.ch", true)), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("neworg.zitadel.ch")), - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("org.zitadel.ch")), + org.NewOrgChangedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "org", "neworg", + ), + org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch", + ), + org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch", + ), + org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "neworg.zitadel.ch", + ), + org.NewDomainRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, "org.zitadel.ch", true, + ), ), ), }, @@ -812,10 +794,9 @@ func TestCommandSide_DeactivateOrg(t *testing.T) { ), expectPushFailed( errors.ThrowInternal(nil, "id", "message"), - []*repository.Event{ - eventFromEventPusher(org.NewOrgDeactivatedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate)), - }, + org.NewOrgDeactivatedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + ), ), ), }, @@ -840,11 +821,9 @@ func TestCommandSide_DeactivateOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgDeactivatedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - )), - }, + org.NewOrgDeactivatedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + ), ), ), }, @@ -871,6 +850,7 @@ func TestCommandSide_DeactivateOrg(t *testing.T) { }) } } + func TestCommandSide_ReactivateOrg(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore @@ -948,11 +928,9 @@ func TestCommandSide_ReactivateOrg(t *testing.T) { ), expectPushFailed( errors.ThrowInternal(nil, "id", "message"), - []*repository.Event{ - eventFromEventPusher(org.NewOrgReactivatedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - )), - }, + org.NewOrgReactivatedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + ), ), ), }, @@ -981,10 +959,9 @@ func TestCommandSide_ReactivateOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgReactivatedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate)), - }, + org.NewOrgReactivatedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + ), ), ), }, @@ -1117,14 +1094,9 @@ func TestCommandSide_RemoveOrg(t *testing.T) { expectFilter(), expectPushFailed( errors.ThrowInternal(nil, "id", "message"), - []*repository.Event{ - eventFromEventPusher( - org.NewOrgRemovedEvent( - context.Background(), &org.NewAggregate("org1").Aggregate, "org", []string{}, false, []string{}, []*domain.UserIDPLink{}, []string{}, - ), - ), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), + org.NewOrgRemovedEvent( + context.Background(), &org.NewAggregate("org1").Aggregate, "org", []string{}, false, []string{}, []*domain.UserIDPLink{}, []string{}, + ), ), ), }, @@ -1164,14 +1136,9 @@ func TestCommandSide_RemoveOrg(t *testing.T) { expectFilter(), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewOrgRemovedEvent( - context.Background(), &org.NewAggregate("org1").Aggregate, "org", []string{}, false, []string{}, []*domain.UserIDPLink{}, []string{}, - ), - ), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), + org.NewOrgRemovedEvent( + context.Background(), &org.NewAggregate("org1").Aggregate, "org", []string{}, false, []string{}, []*domain.UserIDPLink{}, []string{}, + ), ), ), }, @@ -1199,7 +1166,7 @@ func TestCommandSide_RemoveOrg(t *testing.T) { eventFromEventPusher( org.NewDomainPolicyAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - true, + false, true, true, ), @@ -1217,7 +1184,7 @@ func TestCommandSide_RemoveOrg(t *testing.T) { language.German, domain.GenderMale, "email1", - true, + false, ), ), eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -1225,7 +1192,7 @@ func TestCommandSide_RemoveOrg(t *testing.T) { "user2", "name", "description", - true, + false, domain.OIDCTokenTypeBearer, ), ), @@ -1255,26 +1222,13 @@ func TestCommandSide_RemoveOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - org.NewOrgRemovedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, "org", - []string{"user1", "user2"}, - false, - []string{"domain1", "domain2"}, - []*domain.UserIDPLink{{IDPConfigID: "config1", ExternalUserID: "id1", DisplayName: "display1"}, {IDPConfigID: "config2", ExternalUserID: "id2", DisplayName: "display2"}}, - []string{"entity1", "entity2"}, - ), - ), - }, - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")), - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user1", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("user2", "org1", true)), - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("domain1")), - uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("domain2")), - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("config1", "id1")), - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("config2", "id2")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("entity1")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("entity2")), + org.NewOrgRemovedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, "org", + []string{"user1", "user2"}, + false, + []string{"domain1", "domain2"}, + []*domain.UserIDPLink{{IDPConfigID: "config1", ExternalUserID: "id1", DisplayName: "display1"}, {IDPConfigID: "config2", ExternalUserID: "id2", DisplayName: "display2"}}, + []string{"entity1", "entity2"}, + ), ), ), }, @@ -1428,64 +1382,58 @@ func TestCommandSide_SetUpOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "Org", - )), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "username", - "firstname", - "lastname", - "", - "firstname lastname", - language.English, - domain.GenderUnspecified, - "email@test.ch", - true, - ), + eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "Org", + )), + eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "username", + "firstname", + "lastname", + "", + "firstname lastname", + language.English, + domain.GenderUnspecified, + "email@test.ch", + true, ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - ), + ), + eventFromEventPusher( + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("userinit"), - }, - 1*time.Hour, - ), + ), + eventFromEventPusher( + user.NewHumanInitialCodeAddedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("userinit"), + }, + 1*time.Hour, ), - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "userID", - domain.RoleOrgOwner, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "orgID", true)), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("orgID", "userID")), + ), + eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "userID", + domain.RoleOrgOwner, + )), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID", "userID"), @@ -1547,31 +1495,26 @@ func TestCommandSide_SetUpOrg(t *testing.T) { ), expectFilter(), // org member check expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "Org", - )), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "userID", - domain.RoleOrgOwner, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("orgID", "userID")), + eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "Org", + )), + eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "userID", + domain.RoleOrgOwner, + )), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID"), @@ -1632,50 +1575,44 @@ func TestCommandSide_SetUpOrg(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "Org", - )), - eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "org.iam-domain", - )), - eventFromEventPusher( - user.NewMachineAddedEvent(context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "username", - "name", - "description", - true, - domain.OIDCTokenTypeBearer, - ), + eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "Org", + )), + eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher( + user.NewMachineAddedEvent(context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "username", + "name", + "description", + true, + domain.OIDCTokenTypeBearer, ), - eventFromEventPusher( - user.NewPersonalAccessTokenAddedEvent(context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - testNow.Add(time.Hour), - []string{openid.ScopeOpenID}, - ), + ), + eventFromEventPusher( + user.NewPersonalAccessTokenAddedEvent(context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", + testNow.Add(time.Hour), + []string{openid.ScopeOpenID}, ), - eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), - &org.NewAggregate("orgID").Aggregate, - "userID", - domain.RoleOrgOwner, - )), - }, - uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("Org")), - uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("org.iam-domain")), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "orgID", true)), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("orgID", "userID")), + ), + eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "userID", + domain.RoleOrgOwner, + )), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID", "userID", "tokenID"), diff --git a/internal/command/preparation/command_test.go b/internal/command/preparation/command_test.go index 7ccd59070..e7b0cb65c 100644 --- a/internal/command/preparation/command_test.go +++ b/internal/command/preparation/command_test.go @@ -167,10 +167,10 @@ type testCommand struct { eventstore.BaseEvent } -func (c *testCommand) Data() interface{} { +func (c *testCommand) Payload() interface{} { return nil } -func (c *testCommand) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (c *testCommand) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/command/project.go b/internal/command/project.go index 751d54535..a561daa90 100644 --- a/internal/command/project.go +++ b/internal/command/project.go @@ -282,7 +282,7 @@ func (c *Commands) RemoveProject(ctx context.Context, projectID, resourceOwner s return nil, err } - uniqueConstraints := make([]*eventstore.EventUniqueConstraint, len(samlEntityIDsAgg.EntityIDs)) + uniqueConstraints := make([]*eventstore.UniqueConstraint, len(samlEntityIDsAgg.EntityIDs)) for i, entityID := range samlEntityIDsAgg.EntityIDs { uniqueConstraints[i] = project.NewRemoveSAMLConfigEntityIDUniqueConstraint(entityID.EntityID) } diff --git a/internal/command/project_application_api_test.go b/internal/command/project_application_api_test.go index faf468a6e..06cfa449c 100644 --- a/internal/command/project_application_api_test.go +++ b/internal/command/project_application_api_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -248,29 +247,22 @@ func TestCommandSide_AddAPIApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewApplicationAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - ), - ), - eventFromEventPusher( - project.NewAPIConfigAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "client1@project", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - domain.APIAuthMethodTypeBasic), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), + project.NewApplicationAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + ), + project.NewAPIConfigAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "client1@project", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + domain.APIAuthMethodTypeBasic), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"), @@ -316,24 +308,17 @@ func TestCommandSide_AddAPIApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewApplicationAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - ), - ), - eventFromEventPusher( - project.NewAPIConfigAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "client1@project", - nil, - domain.APIAuthMethodTypePrivateKeyJWT), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), + project.NewApplicationAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + ), + project.NewAPIConfigAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "client1@project", + nil, + domain.APIAuthMethodTypePrivateKeyJWT), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"), @@ -540,15 +525,11 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAPIAppChangedEvent(context.Background(), - "app1", - "project1", - "org1", - domain.APIAuthMethodTypePrivateKeyJWT), - ), - }, + newAPIAppChangedEvent(context.Background(), + "app1", + "project1", + "org1", + domain.APIAuthMethodTypePrivateKeyJWT), ), ), }, @@ -698,19 +679,16 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewAPIConfigSecretChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }), - ), - }, + project.NewAPIConfigSecretChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), ), ), }, diff --git a/internal/command/project_application_oidc_test.go b/internal/command/project_application_oidc_test.go index 86b3c6e04..85cdf5d5b 100644 --- a/internal/command/project_application_oidc_test.go +++ b/internal/command/project_application_oidc_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -294,44 +293,37 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewApplicationAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - ), - ), - eventFromEventPusher( - project.NewOIDCConfigAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - domain.OIDCVersionV1, - "app1", - "client1@project", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - []string{"https://test.ch"}, - []domain.OIDCResponseType{domain.OIDCResponseTypeCode}, - []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode}, - domain.OIDCApplicationTypeWeb, - domain.OIDCAuthMethodTypePost, - []string{"https://test.ch/logout"}, - true, - domain.OIDCTokenTypeBearer, - true, - true, - true, - time.Second*1, - []string{"https://sub.test.ch"}, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), + project.NewApplicationAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + ), + project.NewOIDCConfigAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + domain.OIDCVersionV1, + "app1", + "client1@project", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + []string{"https://test.ch"}, + []domain.OIDCResponseType{domain.OIDCResponseTypeCode}, + []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode}, + domain.OIDCApplicationTypeWeb, + domain.OIDCAuthMethodTypePost, + []string{"https://test.ch/logout"}, + true, + domain.OIDCTokenTypeBearer, + true, + true, + true, + time.Second*1, + []string{"https://sub.test.ch"}, + true, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"), @@ -643,14 +635,10 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newOIDCAppChangedEvent(context.Background(), - "app1", - "project1", - "org1"), - ), - }, + newOIDCAppChangedEvent(context.Background(), + "app1", + "project1", + "org1"), ), ), }, @@ -844,19 +832,16 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewOIDCConfigSecretChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }), - ), - }, + project.NewOIDCConfigSecretChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), ), ), }, diff --git a/internal/command/project_application_saml_test.go b/internal/command/project_application_saml_test.go index e2ea8e9d1..a9b74a99e 100644 --- a/internal/command/project_application_saml_test.go +++ b/internal/command/project_application_saml_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -185,26 +184,18 @@ func TestCommandSide_AddSAMLApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewApplicationAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - ), - ), - eventFromEventPusher( - project.NewSAMLConfigAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "https://test.com/saml/metadata", - testMetadata, - "", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), - uniqueConstraintsFromEventConstraint(project.NewAddSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), + project.NewApplicationAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + ), + project.NewSAMLConfigAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "https://test.com/saml/metadata", + testMetadata, + "", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1"), @@ -251,26 +242,18 @@ func TestCommandSide_AddSAMLApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewApplicationAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - ), - ), - eventFromEventPusher( - project.NewSAMLConfigAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "https://test.com/saml/metadata", - testMetadata, - "http://localhost:8080/saml/metadata", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), - uniqueConstraintsFromEventConstraint(project.NewAddSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), + project.NewApplicationAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + ), + project.NewSAMLConfigAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "https://test.com/saml/metadata", + testMetadata, + "http://localhost:8080/saml/metadata", + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1"), @@ -584,20 +567,14 @@ func TestCommandSide_ChangeSAMLApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newSAMLAppChangedEventMetadataURL(context.Background(), - "app1", - "project1", - "org1", - "https://test.com/saml/metadata", - "https://test2.com/saml/metadata", - testMetadataChangedEntityID, - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), - uniqueConstraintsFromEventConstraint(project.NewAddSAMLConfigEntityIDUniqueConstraint("https://test2.com/saml/metadata")), + newSAMLAppChangedEventMetadataURL(context.Background(), + "app1", + "project1", + "org1", + "https://test.com/saml/metadata", + "https://test2.com/saml/metadata", + testMetadataChangedEntityID, + ), ), ), httpClient: newTestClient(http.StatusOK, testMetadataChangedEntityID), @@ -656,20 +633,14 @@ func TestCommandSide_ChangeSAMLApplication(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newSAMLAppChangedEventMetadata(context.Background(), - "app1", - "project1", - "org1", - "https://test.com/saml/metadata", - "https://test2.com/saml/metadata", - testMetadataChangedEntityID, - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), - uniqueConstraintsFromEventConstraint(project.NewAddSAMLConfigEntityIDUniqueConstraint("https://test2.com/saml/metadata")), + newSAMLAppChangedEventMetadata(context.Background(), + "app1", + "project1", + "org1", + "https://test.com/saml/metadata", + "https://test2.com/saml/metadata", + testMetadataChangedEntityID, + ), ), ), httpClient: nil, diff --git a/internal/command/project_application_test.go b/internal/command/project_application_test.go index 107900608..95e808dd5 100644 --- a/internal/command/project_application_test.go +++ b/internal/command/project_application_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/project" ) @@ -153,16 +152,12 @@ func TestCommandSide_ChangeApplication(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewApplicationChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - "app changed", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveApplicationUniqueConstraint("app", "project1")), - uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app changed", "project1")), + project.NewApplicationChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + "app changed", + ), ), ), }, @@ -314,12 +309,10 @@ func TestCommandSide_DeactivateApplication(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewApplicationDeactivatedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - )), - }, + project.NewApplicationDeactivatedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + ), ), ), }, @@ -468,12 +461,10 @@ func TestCommandSide_ReactivateApplication(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewApplicationReactivatedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - )), - }, + project.NewApplicationReactivatedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + ), ), ), }, @@ -608,16 +599,12 @@ func TestCommandSide_RemoveApplication(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewApplicationRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - "https://test.com/saml/metadata", - )), - }, /**/ - uniqueConstraintsFromEventConstraint(project.NewRemoveApplicationUniqueConstraint("app", "project1")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), + project.NewApplicationRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + "https://test.com/saml/metadata", + ), ), ), }, @@ -648,15 +635,12 @@ func TestCommandSide_RemoveApplication(t *testing.T) { // app is not saml, or no saml config available expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewApplicationRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "app1", - "app", - "", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveApplicationUniqueConstraint("app", "project1")), + project.NewApplicationRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "app1", + "app", + "", + ), ), ), }, diff --git a/internal/command/project_grant_member_test.go b/internal/command/project_grant_member_test.go index 1282ba1a0..da00b8ccd 100644 --- a/internal/command/project_grant_member_test.go +++ b/internal/command/project_grant_member_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" @@ -179,15 +178,12 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) { ), expectFilter(), expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusher(project.NewProjectGrantMemberAddedEvent(context.Background(), - &project.NewAggregate("project1", "").Aggregate, - "user1", - "projectgrant1", - []string{"PROJECT_GRANT_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")), + project.NewProjectGrantMemberAddedEvent(context.Background(), + &project.NewAggregate("project1", "").Aggregate, + "user1", + "projectgrant1", + []string{"PROJECT_GRANT_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -234,15 +230,12 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectGrantMemberAddedEvent(context.Background(), - &project.NewAggregate("project1", "").Aggregate, - "user1", - "projectgrant1", - []string{"PROJECT_GRANT_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")), + project.NewProjectGrantMemberAddedEvent(context.Background(), + &project.NewAggregate("project1", "").Aggregate, + "user1", + "projectgrant1", + []string{"PROJECT_GRANT_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -435,14 +428,12 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectGrantMemberChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - "projectgrant1", - []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"}..., - )), - }, + project.NewProjectGrantMemberChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + "projectgrant1", + []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -603,14 +594,11 @@ func TestCommandSide_RemoveProjectGrantMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectGrantMemberRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - "projectgrant1", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantMemberUniqueConstraint("project1", "user1", "projectgrant1")), + project.NewProjectGrantMemberRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + "projectgrant1", + ), ), ), }, diff --git a/internal/command/project_grant_test.go b/internal/command/project_grant_test.go index 2ca78ce2a..af74c8db0 100644 --- a/internal/command/project_grant_test.go +++ b/internal/command/project_grant_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -176,15 +175,12 @@ func TestCommandSide_AddProjectGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - "grantedorg1", - []string{"key1"}, - )), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectGrantUniqueConstraint("grantedorg1", "project1")), + project.NewGrantAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + "grantedorg1", + []string{"key1"}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "projectgrant1"), @@ -513,13 +509,11 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - []string{"key1", "key2"}, - )), - }, + project.NewGrantChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + []string{"key1", "key2"}, + ), ), ), }, @@ -594,13 +588,11 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - []string{"key1"}, - )), - }, + project.NewGrantChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + []string{"key1"}, + ), ), ), }, @@ -685,17 +677,15 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - []string{"key1"}, - )), - eventFromEventPusher(usergrant.NewUserGrantCascadeChangedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - []string{"key1"}, - )), - }, + project.NewGrantChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + []string{"key1"}, + ), + usergrant.NewUserGrantCascadeChangedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + []string{"key1"}, + ), ), ), }, @@ -903,12 +893,10 @@ func TestCommandSide_DeactivateProjectGrant(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantDeactivateEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - )), - }, + project.NewGrantDeactivateEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + ), ), ), }, @@ -1102,12 +1090,10 @@ func TestCommandSide_ReactivateProjectGrant(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantReactivatedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - )), - }, + project.NewGrantReactivatedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + ), ), ), }, @@ -1264,14 +1250,11 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - "grantedorg1", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantUniqueConstraint("grantedorg1", "project1")), + project.NewGrantRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + "grantedorg1", + ), ), ), }, @@ -1311,14 +1294,11 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - "grantedorg1", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantUniqueConstraint("grantedorg1", "project1")), + project.NewGrantRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + "grantedorg1", + ), ), ), }, @@ -1365,21 +1345,17 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) { "projectgrant1", []string{"key1"}))), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewGrantRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - "grantedorg1", - )), - eventFromEventPusher(usergrant.NewUserGrantCascadeRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "projectgrant1", - )), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantUniqueConstraint("grantedorg1", "project1")), - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user1", "project1", "projectgrant1")), + project.NewGrantRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + "grantedorg1", + ), + usergrant.NewUserGrantCascadeRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "projectgrant1", + ), ), ), }, diff --git a/internal/command/project_member_test.go b/internal/command/project_member_test.go index db46c7a7b..fd6a4105e 100644 --- a/internal/command/project_member_test.go +++ b/internal/command/project_member_test.go @@ -9,9 +9,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" "golang.org/x/text/language" @@ -181,14 +179,11 @@ func TestCommandSide_AddProjectMember(t *testing.T) { ), expectFilter(), expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusher(project.NewProjectMemberAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - []string{"PROJECT_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")), + project.NewProjectMemberAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + []string{"PROJECT_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -235,14 +230,11 @@ func TestCommandSide_AddProjectMember(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectMemberAddedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - []string{"PROJECT_OWNER"}..., - )), - }, - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")), + project.NewProjectMemberAddedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + []string{"PROJECT_OWNER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -435,13 +427,11 @@ func TestCommandSide_ChangeProjectMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectMemberChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - []string{"PROJECT_OWNER", "PROJECT_VIEWER"}..., - )), - }, + project.NewProjectMemberChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + []string{"PROJECT_OWNER", "PROJECT_VIEWER"}..., + ), ), ), zitadelRoles: []authz.RoleMapping{ @@ -583,13 +573,10 @@ func TestCommandSide_RemoveProjectMember(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectMemberRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - )), - }, - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")), + project.NewProjectMemberRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + ), ), ), }, diff --git a/internal/command/project_role_test.go b/internal/command/project_role_test.go index e7b752fec..2d40cdc91 100644 --- a/internal/command/project_role_test.go +++ b/internal/command/project_role_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/usergrant" @@ -115,17 +114,13 @@ func TestCommandSide_AddProjectRole(t *testing.T) { ), ), expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "id", "internal"), - []*repository.Event{ - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - "key", - "group", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + "key", + "group", + ), ), ), }, @@ -160,17 +155,13 @@ func TestCommandSide_AddProjectRole(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - "key", - "group", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + "key", + "group", + ), ), ), }, @@ -325,26 +316,20 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) { ), ), expectPushFailed(caos_errs.ThrowAlreadyExists(nil, "id", "internal"), - []*repository.Event{ - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - "key", - "group", - ), - ), - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key2", - "key2", - "group", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key1", "project1")), - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key2", "project1")), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + "key", + "group", + ), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key2", + "key2", + "group", + ), ), ), }, @@ -384,26 +369,20 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - "key", - "group", - ), - ), - eventFromEventPusher(project.NewRoleAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key2", - "key2", - "group", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key1", "project1")), - uniqueConstraintsFromEventConstraint(project.NewAddProjectRoleUniqueConstraint("key2", "project1")), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + "key", + "group", + ), + project.NewRoleAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key2", + "key2", + "group", + ), ), ), }, @@ -641,11 +620,7 @@ func TestCommandSide_ChangeProjectRole(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRoleChangedEvent(context.Background(), "project1", "org1", "key1", "keychanged", "groupchanged"), - ), - }, + newRoleChangedEvent(context.Background(), "project1", "org1", "key1", "keychanged", "groupchanged"), ), ), }, @@ -815,15 +790,10 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewRoleRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + ), ), ), }, @@ -856,15 +826,10 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewRoleRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + ), ), ), }, @@ -907,22 +872,15 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewRoleRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - ), - ), - eventFromEventPusher( - project.NewGrantCascadeChangedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "projectgrant1", - []string{}, - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + ), + project.NewGrantCascadeChangedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "projectgrant1", + []string{}, + ), ), ), }, @@ -956,15 +914,10 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewRoleRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + ), ), ), }, @@ -1006,21 +959,14 @@ func TestCommandSide_RemoveProjectRole(t *testing.T) { []string{"key1"})), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewRoleRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "key1", - ), - ), - eventFromEventPusher( - usergrant.NewUserGrantCascadeChangedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - []string{}, - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectRoleUniqueConstraint("key1", "project1")), + project.NewRoleRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "key1", + ), + usergrant.NewUserGrantCascadeChangedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + []string{}, + ), ), ), }, diff --git a/internal/command/project_test.go b/internal/command/project_test.go index 60b77f9e2..715b82f08 100644 --- a/internal/command/project_test.go +++ b/internal/command/project_test.go @@ -9,11 +9,9 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" - "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/project" ) @@ -60,24 +58,18 @@ func TestCommandSide_AddProject(t *testing.T) { eventstore: eventstoreExpect( t, expectPushFailed(errors.ThrowAlreadyExists(nil, "ERROR", "internl"), - []*repository.Event{ - eventFromEventPusher(project.NewProjectAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "project", true, true, true, - domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy, - ), - ), - eventFromEventPusher(project.NewProjectMemberAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - []string{domain.RoleProjectOwner}..., - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectNameUniqueConstraint("project", "org1")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")), + project.NewProjectAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "project", true, true, true, + domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy, + ), + project.NewProjectMemberAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + []string{domain.RoleProjectOwner}..., + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "project1"), @@ -104,24 +96,18 @@ func TestCommandSide_AddProject(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusher(project.NewProjectAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "project", true, true, true, - domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy, - ), - ), - eventFromEventPusher(project.NewProjectMemberAddedEvent( - context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - []string{domain.RoleProjectOwner}..., - ), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewAddProjectNameUniqueConstraint("project", "org1")), - uniqueConstraintsFromEventConstraint(member.NewAddMemberUniqueConstraint("project1", "user1")), + project.NewProjectAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "project", true, true, true, + domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy, + ), + project.NewProjectMemberAddedEvent( + context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + []string{domain.RoleProjectOwner}..., + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "project1"), @@ -334,21 +320,16 @@ func TestCommandSide_ChangeProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newProjectChangedEvent(context.Background(), - "project1", - "org1", - "project", - "project-new", - false, - false, - false, - domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectNameUniqueConstraint("project", "org1")), - uniqueConstraintsFromEventConstraint(project.NewAddProjectNameUniqueConstraint("project-new", "org1")), + newProjectChangedEvent(context.Background(), + "project1", + "org1", + "project", + "project-new", + false, + false, + false, + domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy, + ), ), ), }, @@ -394,19 +375,16 @@ func TestCommandSide_ChangeProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newProjectChangedEvent(context.Background(), - "project1", - "org1", - "project", - "", - false, - false, - false, - domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy), - ), - }, + newProjectChangedEvent(context.Background(), + "project1", + "org1", + "", + "", + false, + false, + false, + domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy, + ), ), ), }, @@ -598,12 +576,8 @@ func TestCommandSide_DeactivateProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewProjectDeactivatedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate), - ), - }, + project.NewProjectDeactivatedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate), ), ), }, @@ -778,12 +752,8 @@ func TestCommandSide_ReactivateProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewProjectReactivatedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate), - ), - }, + project.NewProjectReactivatedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate), ), ), }, @@ -932,15 +902,10 @@ func TestCommandSide_RemoveProject(t *testing.T) { // no saml application events expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewProjectRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "project", - nil), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectNameUniqueConstraint("project", "org1")), + project.NewProjectRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "project", + nil), ), ), }, @@ -985,18 +950,13 @@ func TestCommandSide_RemoveProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewProjectRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "project", - []*eventstore.EventUniqueConstraint{ - project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata"), - }), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectNameUniqueConstraint("project", "org1")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata")), + project.NewProjectRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "project", + []*eventstore.UniqueConstraint{ + project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test.com/saml/metadata"), + }, + ), ), ), }, @@ -1069,22 +1029,15 @@ func TestCommandSide_RemoveProject(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - project.NewProjectRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "project", - []*eventstore.EventUniqueConstraint{ - project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test1.com/saml/metadata"), - project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test2.com/saml/metadata"), - project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test3.com/saml/metadata"), - }), - ), - }, - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectNameUniqueConstraint("project", "org1")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test1.com/saml/metadata")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test2.com/saml/metadata")), - uniqueConstraintsFromEventConstraint(project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test3.com/saml/metadata")), + project.NewProjectRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "project", + []*eventstore.UniqueConstraint{ + project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test1.com/saml/metadata"), + project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test2.com/saml/metadata"), + project.NewRemoveSAMLConfigEntityIDUniqueConstraint("https://test3.com/saml/metadata"), + }, + ), ), ), }, @@ -1219,103 +1172,3 @@ func TestAddProject(t *testing.T) { }) } } - -// func TestExistsProject(t *testing.T) { -// type args struct { -// filter preparation.FilterToQueryReducer -// id string -// resourceOwner string -// } -// tests := []struct { -// name string -// args args -// wantExists bool -// wantErr bool -// }{ -// { -// name: "no events", -// args: args{ -// filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { -// return []eventstore.Event{}, nil -// }, -// id: "id", -// resourceOwner: "ro", -// }, -// wantExists: false, -// wantErr: false, -// }, -// { -// name: "project added", -// args: args{ -// filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { -// return []eventstore.Event{ -// project.NewProjectAddedEvent( -// context.Background(), -// &project.NewAggregate("id", "ro").Aggregate, -// "name", -// false, -// false, -// false, -// domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy, -// ), -// }, nil -// }, -// id: "id", -// resourceOwner: "ro", -// }, -// wantExists: true, -// wantErr: false, -// }, -// { -// name: "project removed", -// args: args{ -// filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { -// return []eventstore.Event{ -// project.NewProjectAddedEvent( -// context.Background(), -// &project.NewAggregate("id", "ro").Aggregate, -// "name", -// false, -// false, -// false, -// domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy, -// ), -// project.NewProjectRemovedEvent( -// context.Background(), -// &project.NewAggregate("id", "ro").Aggregate, -// "name", -// ), -// }, nil -// }, -// id: "id", -// resourceOwner: "ro", -// }, -// wantExists: false, -// wantErr: false, -// }, -// { -// name: "error durring filter", -// args: args{ -// filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { -// return nil, errors.ThrowInternal(nil, "PROJE-Op26p", "Errors.Internal") -// }, -// id: "id", -// resourceOwner: "ro", -// }, -// wantExists: false, -// wantErr: true, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// gotExists, err := projectWriteModel(context.Background(), tt.args.filter, tt.args.id, tt.args.resourceOwner) -// if (err != nil) != tt.wantErr { -// t.Errorf("ExistsUser() error = %v, wantErr %v", err, tt.wantErr) -// return -// } -// if gotExists != tt.wantExists { -// t.Errorf("ExistsUser() = %v, want %v", gotExists, tt.wantExists) -// } -// }) -// } -// } diff --git a/internal/command/quota_model.go b/internal/command/quota_model.go index 23893ea6a..3c4c87c40 100644 --- a/internal/command/quota_model.go +++ b/internal/command/quota_model.go @@ -37,8 +37,8 @@ func newQuotaWriteModel(instanceId, resourceOwner string, unit quota.Unit) *quot func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder { query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). ResourceOwner(wm.ResourceOwner). - AddQuery(). InstanceID(wm.InstanceID). + AddQuery(). AggregateTypes(quota.AggregateType). EventTypes( quota.AddedEventType, @@ -51,7 +51,7 @@ func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder { func (wm *quotaWriteModel) Reduce() error { for _, event := range wm.Events { - wm.ChangeDate = event.CreationDate() + wm.ChangeDate = event.CreatedAt() switch e := event.(type) { case *quota.SetEvent: wm.rollingAggregateID = e.Aggregate().ID diff --git a/internal/command/quota_report_test.go b/internal/command/quota_report_test.go index 214b67ac7..6c942a3bd 100644 --- a/internal/command/quota_report_test.go +++ b/internal/command/quota_report_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/quota" @@ -68,14 +67,16 @@ func TestQuotaReport_ReportQuotaUsage(t *testing.T) { args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), dueNotifications: []*quota.NotificationDueEvent{ - { - Unit: QuotaRequestsAllAuthenticated.Enum(), - ID: "id", - CallURL: "url", - PeriodStart: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - Threshold: 1000, - Usage: 250, - }, + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), }, }, res: res{}, @@ -87,20 +88,15 @@ func TestQuotaReport_ReportQuotaUsage(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewNotificationDueEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - QuotaRequestsAllAuthenticated.Enum(), - "id", - "url", - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 1000, - 250, - ), - ), - }, + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), ), ), }, @@ -143,32 +139,24 @@ func TestQuotaReport_ReportQuotaUsage(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewNotificationDueEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - QuotaRequestsAllAuthenticated.Enum(), - "id1", - "url", - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 1000, - 250, - ), - ), - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewNotificationDueEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - QuotaRequestsAllAuthenticated.Enum(), - "id3", - "url", - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 1000, - 250, - ), - ), - }, + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), + quota.NewNotificationDueEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id3", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, + ), ), ), }, @@ -250,25 +238,20 @@ func TestQuotaReport_UsageNotificationSent(t *testing.T) { eventstore: eventstoreExpect( t, expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewNotifiedEvent( - context.Background(), - "quota1", - quota.NewNotificationDueEvent( - context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - QuotaRequestsAllAuthenticated.Enum(), - "id1", - "url", - time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), - 1000, - 250, - ), - ), + quota.NewNotifiedEvent( + context.Background(), + "quota1", + quota.NewNotificationDueEvent( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + "id1", + "url", + time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC), + 1000, + 250, ), - }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), diff --git a/internal/command/quota_test.go b/internal/command/quota_test.go index 75c8e6bdb..5b28f3e61 100644 --- a/internal/command/quota_test.go +++ b/internal/command/quota_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/quota" @@ -108,24 +107,19 @@ func TestQuota_AddQuota(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), @@ -178,24 +172,19 @@ func TestQuota_AddQuota(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota2", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota2", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"), @@ -224,31 +213,26 @@ func TestQuota_AddQuota(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications( - []*quota.SetEventNotification{{ - ID: "notification1", - Percent: 20, - Repeat: false, - CallURL: "https://url.com", - }}, - ), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications( + []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }}, + ), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"), @@ -389,24 +373,19 @@ func TestQuota_SetQuota(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"), @@ -459,24 +438,19 @@ func TestQuota_SetQuota(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota2", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota2", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"), @@ -505,31 +479,26 @@ func TestQuota_SetQuota(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewSetEvent( - eventstore.NewBaseEventForPush( - context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - quota.SetEventType, - ), - QuotaRequestsAllAuthenticated.Enum(), - quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), - quota.ChangeResetInterval(30*24*time.Hour), - quota.ChangeAmount(1000), - quota.ChangeLimit(true), - quota.ChangeNotifications( - []*quota.SetEventNotification{{ - ID: "notification1", - Percent: 20, - Repeat: false, - CallURL: "https://url.com", - }}, - ), - ), + quota.NewSetEvent( + eventstore.NewBaseEventForPush( + context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + quota.SetEventType, ), - }, + QuotaRequestsAllAuthenticated.Enum(), + quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)), + quota.ChangeResetInterval(30*24*time.Hour), + quota.ChangeAmount(1000), + quota.ChangeLimit(true), + quota.ChangeNotifications( + []*quota.SetEventNotification{{ + ID: "notification1", + Percent: 20, + Repeat: false, + CallURL: "https://url.com", + }}, + ), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"), @@ -678,16 +647,10 @@ func TestQuota_RemoveQuota(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - quota.NewRemovedEvent(context.Background(), - "a.NewAggregate("quota1", "INSTANCE").Aggregate, - QuotaRequestsAllAuthenticated.Enum(), - ), - ), - }, - uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewRemoveQuotaNameUniqueConstraint(quota.RequestsAllAuthenticated)), + quota.NewRemovedEvent(context.Background(), + "a.NewAggregate("quota1", "INSTANCE").Aggregate, + QuotaRequestsAllAuthenticated.Enum(), + ), ), ), }, diff --git a/internal/command/session_otp_test.go b/internal/command/session_otp_test.go index 9cf957945..d01cd4069 100644 --- a/internal/command/session_otp_test.go +++ b/internal/command/session_otp_test.go @@ -265,9 +265,7 @@ func TestCommands_OTPSMSSent(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - session.NewOTPSMSSentEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), - ), + session.NewOTPSMSSentEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), ), ), }, @@ -669,9 +667,7 @@ func TestCommands_OTPEmailSent(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - session.NewOTPEmailSentEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), - ), + session.NewOTPEmailSentEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), ), ), }, diff --git a/internal/command/session_test.go b/internal/command/session_test.go index 57f3ba971..e478792e1 100644 --- a/internal/command/session_test.go +++ b/internal/command/session_test.go @@ -204,11 +204,9 @@ func TestCommands_CreateSession(t *testing.T) { []expect{ expectFilter(), expectPush( - eventPusherToEvents( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), - session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - "tokenID", - ), + session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), + session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + "tokenID", ), ), }, @@ -438,15 +436,17 @@ func TestCommands_updateSession(t *testing.T) { fields{ eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - "userID", testNow), - session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - testNow), - session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - map[string][]byte{"key": []byte("value")}), - session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - "tokenID"), + session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + "userID", testNow, + ), + session.NewPasswordCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + testNow, + ), + session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + map[string][]byte{"key": []byte("value")}, + ), + session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + "tokenID", ), ), ), @@ -620,16 +620,14 @@ func TestCommands_updateSession(t *testing.T) { fields{ eventstore: eventstoreExpect(t, expectPush( - eventPusherToEvents( - session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - "userID", testNow), - session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - testNow), - session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - map[string][]byte{"key": []byte("value")}), - session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, - "tokenID"), - ), + session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + "userID", testNow), + session.NewIntentCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + testNow), + session.NewMetadataSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + map[string][]byte{"key": []byte("value")}), + session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, + "tokenID"), ), ), }, @@ -930,8 +928,7 @@ func TestCommands_TerminateSession(t *testing.T) { ), expectPushFailed( caos_errs.ThrowInternal(nil, "id", "pushed failed"), - eventPusherToEvents( - session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { @@ -960,8 +957,7 @@ func TestCommands_TerminateSession(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewTerminateEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { diff --git a/internal/command/sms_config_model.go b/internal/command/sms_config_model.go index 3db99679c..2c215ebf6 100644 --- a/internal/command/sms_config_model.go +++ b/internal/command/sms_config_model.go @@ -28,6 +28,7 @@ func NewIAMSMSConfigWriteModel(instanceID, id string) *IAMSMSConfigWriteModel { WriteModel: eventstore.WriteModel{ AggregateID: instanceID, ResourceOwner: instanceID, + InstanceID: instanceID, }, ID: id, } diff --git a/internal/command/sms_config_test.go b/internal/command/sms_config_test.go index 99f738c32..a6df65cb7 100644 --- a/internal/command/sms_config_test.go +++ b/internal/command/sms_config_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/notification/channels/twilio" @@ -46,22 +45,19 @@ func TestCommandSide_AddSMSConfigTwilio(t *testing.T) { t, expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher(instance.NewSMSConfigTwilioAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "providerid", - "sid", - "senderName", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("token"), - }, - ), - ), - }, + instance.NewSMSConfigTwilioAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "providerid", + "sid", + "senderName", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("token"), + }, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "providerid"), @@ -218,16 +214,12 @@ func TestCommandSide_ChangeSMSConfigTwilio(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newSMSConfigTwilioChangedEvent( - context.Background(), - "providerid", - "sid2", - "senderName2", - ), - ), - }, + newSMSConfigTwilioChangedEvent( + context.Background(), + "providerid", + "sid2", + "senderName2", + ), ), ), }, @@ -335,15 +327,11 @@ func TestCommandSide_ActivateSMSConfigTwilio(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewSMSConfigTwilioActivatedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "providerid", - ), - ), - }, + instance.NewSMSConfigTwilioActivatedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "providerid", + ), ), ), }, @@ -453,15 +441,11 @@ func TestCommandSide_DeactivateSMSConfigTwilio(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewSMSConfigDeactivatedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "providerid", - ), - ), - }, + instance.NewSMSConfigDeactivatedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "providerid", + ), ), ), }, @@ -564,15 +548,11 @@ func TestCommandSide_RemoveSMSConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - instance.NewSMSConfigRemovedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "providerid", - ), - ), - }, + instance.NewSMSConfigRemovedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "providerid", + ), ), ), }, diff --git a/internal/command/smtp.go b/internal/command/smtp.go index 664cbc4bc..2f7ea95f2 100644 --- a/internal/command/smtp.go +++ b/internal/command/smtp.go @@ -28,7 +28,7 @@ func (c *Commands) AddSMTPConfig(ctx context.Context, config *smtp.Config) (*dom } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -46,7 +46,7 @@ func (c *Commands) ChangeSMTPConfig(ctx context.Context, config *smtp.Config) (* } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -76,7 +76,7 @@ func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, password string } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } @@ -94,7 +94,7 @@ func (c *Commands) RemoveSMTPConfig(ctx context.Context) (*domain.ObjectDetails, } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().InstanceID, }, nil } diff --git a/internal/command/smtp_test.go b/internal/command/smtp_test.go index 1832b2d9d..39f38a37b 100644 --- a/internal/command/smtp_test.go +++ b/internal/command/smtp_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/notification/channels/smtp" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -143,27 +142,22 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSMTPConfigAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - "from@domain.ch", - "name", - "", - "host:587", - "user", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - ), - ), - }, + instance.NewSMTPConfigAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + "from@domain.ch", + "name", + "", + "host:587", + "user", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -208,27 +202,22 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSMTPConfigAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - "from@domain.ch", - "name", - "replyto@domain.ch", - "host:587", - "user", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - ), - ), - }, + instance.NewSMTPConfigAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + "from@domain.ch", + "name", + "replyto@domain.ch", + "host:587", + "user", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -318,27 +307,22 @@ func TestCommandSide_AddSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSMTPConfigAddedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - true, - "from@domain.ch", - "name", - "", - "[2001:db8::1]:2525", - "user", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - ), - ), - }, + instance.NewSMTPConfigAddedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + true, + "from@domain.ch", + "name", + "", + "[2001:db8::1]:2525", + "user", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -576,20 +560,15 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newSMTPConfigChangedEvent( - context.Background(), - false, - "from2@domain.ch", - "name2", - "replyto@domain.ch", - "host2:587", - "user2", - ), - ), - }, + newSMTPConfigChangedEvent( + context.Background(), + false, + "from2@domain.ch", + "name2", + "replyto@domain.ch", + "host2:587", + "user2", + ), ), ), }, @@ -690,20 +669,15 @@ func TestCommandSide_ChangeSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - newSMTPConfigChangedEvent( - context.Background(), - false, - "from2@domain.ch", - "name2", - "replyto@domain.ch", - "[2001:db8::1]:2525", - "user2", - ), - ), - }, + newSMTPConfigChangedEvent( + context.Background(), + false, + "from2@domain.ch", + "name2", + "replyto@domain.ch", + "[2001:db8::1]:2525", + "user2", + ), ), ), }, @@ -802,21 +776,16 @@ func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSMTPConfigPasswordChangedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("password"), - }, - ), - ), - }, + instance.NewSMTPConfigPasswordChangedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("password"), + }, + ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -906,15 +875,10 @@ func TestCommandSide_RemoveSMTPConfig(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID( - "INSTANCE", - instance.NewSMTPConfigRemovedEvent( - context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - ), - ), - }, + instance.NewSMTPConfigRemovedEvent( + context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + ), ), ), }, diff --git a/internal/command/unique_constraints_model.go b/internal/command/unique_constraints_model.go index 41cab4ca0..c09f6343c 100644 --- a/internal/command/unique_constraints_model.go +++ b/internal/command/unique_constraints_model.go @@ -256,7 +256,7 @@ func (rm *UniqueConstraintReadModel) getUniqueConstraint(aggregateID, objectID, return nil } -func (rm *UniqueConstraintReadModel) addUniqueConstraint(aggregateID, objectID string, constraint *eventstore.EventUniqueConstraint) { +func (rm *UniqueConstraintReadModel) addUniqueConstraint(aggregateID, objectID string, constraint *eventstore.UniqueConstraint) { migrateUniqueConstraint := &domain.UniqueConstraintMigration{ AggregateID: aggregateID, ObjectID: objectID, @@ -267,7 +267,7 @@ func (rm *UniqueConstraintReadModel) addUniqueConstraint(aggregateID, objectID s rm.UniqueConstraints = append(rm.UniqueConstraints, migrateUniqueConstraint) } -func (rm *UniqueConstraintReadModel) changeUniqueConstraint(aggregateID, objectID string, constraint *eventstore.EventUniqueConstraint) { +func (rm *UniqueConstraintReadModel) changeUniqueConstraint(aggregateID, objectID string, constraint *eventstore.UniqueConstraint) { for i, uniqueConstraint := range rm.UniqueConstraints { if uniqueConstraint.AggregateID == aggregateID && uniqueConstraint.ObjectID == objectID && uniqueConstraint.UniqueType == constraint.UniqueType { rm.UniqueConstraints[i] = &domain.UniqueConstraintMigration{ diff --git a/internal/command/user_grant.go b/internal/command/user_grant.go index 1e1f58396..9acc79dc7 100644 --- a/internal/command/user_grant.go +++ b/internal/command/user_grant.go @@ -140,7 +140,7 @@ func (c *Commands) removeRoleFromUserGrant(ctx context.Context, userGrantID stri func (c *Commands) DeactivateUserGrant(ctx context.Context, grantID, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) { if grantID == "" || resourceOwner == "" { - return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-M0dsf", "Errors.UserGrant.IDMissing") + return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-N2OhG", "Errors.UserGrant.IDMissing") } existingUserGrant, err := c.userGrantWriteModelByID(ctx, grantID, resourceOwner) diff --git a/internal/command/user_grant_test.go b/internal/command/user_grant_test.go index 3a5259820..340c98dc2 100644 --- a/internal/command/user_grant_test.go +++ b/internal/command/user_grant_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -437,16 +436,13 @@ func TestCommandSide_AddUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(usergrant.NewUserGrantAddedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "", - []string{"rolekey1"}, - )), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewAddUserGrantUniqueConstraint("org1", "user1", "project1", "")), + usergrant.NewUserGrantAddedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "", + []string{"rolekey1"}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "usergrant1"), @@ -518,16 +514,13 @@ func TestCommandSide_AddUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(usergrant.NewUserGrantAddedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "projectgrant1", - []string{"rolekey1"}, - )), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewAddUserGrantUniqueConstraint("org1", "user1", "project1", "projectgrant1")), + usergrant.NewUserGrantAddedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "projectgrant1", + []string{"rolekey1"}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "usergrant1"), @@ -1078,12 +1071,10 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(usergrant.NewUserGrantChangedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - []string{"rolekey1", "rolekey2"}, - )), - }, + usergrant.NewUserGrantChangedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + []string{"rolekey1", "rolekey2"}, + ), ), ), }, @@ -1174,12 +1165,10 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher(usergrant.NewUserGrantChangedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - []string{"rolekey1", "rolekey2"}, - )), - }, + usergrant.NewUserGrantChangedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + []string{"rolekey1", "rolekey2"}, + ), ), ), }, @@ -1397,13 +1386,9 @@ func TestCommandSide_DeactivateUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantDeactivatedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - ), - ), - }, + usergrant.NewUserGrantDeactivatedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + ), ), ), }, @@ -1609,13 +1594,9 @@ func TestCommandSide_ReactivateUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantReactivatedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - ), - ), - }, + usergrant.NewUserGrantReactivatedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + ), ), ), }, @@ -1777,17 +1758,12 @@ func TestCommandSide_RemoveUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "", - ), - ), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user1", "project1", "")), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "", + ), ), ), }, @@ -1817,17 +1793,12 @@ func TestCommandSide_RemoveUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "projectgrant1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user1", "project1", "projectgrant1")), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "projectgrant1", + ), ), ), }, @@ -1994,26 +1965,18 @@ func TestCommandSide_BulkRemoveUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "", - ), - ), - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant2", "org1").Aggregate, - "user2", - "project2", - "", - ), - ), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user1", "project1", "")), - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user2", "project2", "")), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "", + ), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant2", "org1").Aggregate, + "user2", + "project2", + "", + ), ), ), }, @@ -2048,26 +2011,18 @@ func TestCommandSide_BulkRemoveUserGrant(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant1", "org1").Aggregate, - "user1", - "project1", - "projectgrant1", - ), - ), - eventFromEventPusher( - usergrant.NewUserGrantRemovedEvent(context.Background(), - &usergrant.NewAggregate("usergrant2", "org1").Aggregate, - "user2", - "project2", - "projectgrant2", - ), - ), - }, - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user1", "project1", "projectgrant1")), - uniqueConstraintsFromEventConstraint(usergrant.NewRemoveUserGrantUniqueConstraint("org1", "user2", "project2", "projectgrant2")), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant1", "org1").Aggregate, + "user1", + "project1", + "projectgrant1", + ), + usergrant.NewUserGrantRemovedEvent(context.Background(), + &usergrant.NewAggregate("usergrant2", "org1").Aggregate, + "user2", + "project2", + "projectgrant2", + ), ), ), }, diff --git a/internal/command/user_human.go b/internal/command/user_human.go index 645d6bf9e..13a553dbb 100644 --- a/internal/command/user_human.go +++ b/internal/command/user_human.go @@ -149,7 +149,7 @@ func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *Ad } human.Details = &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, } diff --git a/internal/command/user_human_adress_test.go b/internal/command/user_human_adress_test.go index db40534fc..d3b3176e3 100644 --- a/internal/command/user_human_adress_test.go +++ b/internal/command/user_human_adress_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -131,18 +130,14 @@ func TestCommandSide_ChangeHumanAddress(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddressChangedEvent(context.Background(), - "user1", "org1", - "country", - "locality", - "postalcode", - "region", - "street", - ), - ), - }, + newAddressChangedEvent(context.Background(), + "user1", "org1", + "country", + "locality", + "postalcode", + "region", + "street", + ), ), ), }, diff --git a/internal/command/user_human_avatar_test.go b/internal/command/user_human_avatar_test.go index 0f6f19d32..ecb7c5b96 100644 --- a/internal/command/user_human_avatar_test.go +++ b/internal/command/user_human_avatar_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/static" "github.com/zitadel/zitadel/internal/static/mock" @@ -151,14 +150,10 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanAvatarAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "avatar?v=test", - ), - ), - }, + user.NewHumanAvatarAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "avatar?v=test", + ), ), ), storage: mock.NewStorage(t).ExpectPutObject(), @@ -326,14 +321,10 @@ func TestCommandSide_RemoveHumanAvatar(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanAvatarRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key", - ), - ), - }, + user.NewHumanAvatarRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key", + ), ), ), }, diff --git a/internal/command/user_human_email_test.go b/internal/command/user_human_email_test.go index 3f41b2b5a..d2861d90f 100644 --- a/internal/command/user_human_email_test.go +++ b/internal/command/user_human_email_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -181,19 +180,13 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -241,13 +234,59 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + email: &domain.Email{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + }, + EmailAddress: "email@test.ch", + IsEmailVerified: true, + }, + resourceOwner: "org1", + }, + res: res{ + want: &domain.Email{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + ResourceOwner: "org1", + }, + EmailAddress: "email@test.ch", + IsEmailVerified: true, + }, + }, + }, + { + name: "email verified, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, ), - }, + ), + ), + expectPush( + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -295,26 +334,20 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -497,13 +530,9 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerificationFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailVerificationFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -552,13 +581,9 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -755,20 +780,16 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanEmailCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -887,13 +908,9 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailCodeSentEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailCodeSentEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_human_init_test.go b/internal/command/user_human_init_test.go index 69fce8366..e221b8665 100644 --- a/internal/command/user_human_init_test.go +++ b/internal/command/user_human_init_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -133,20 +132,16 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -191,20 +186,16 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -248,24 +239,20 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email2@test.ch")), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email2@test.ch", + ), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -442,13 +429,9 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitializedCheckFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanInitializedCheckFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -497,18 +480,12 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitializedCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanInitializedCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -574,19 +551,15 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitializedCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - eventFromEventPusher( - user.NewHumanPasswordChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - false, - "")), - }, + user.NewHumanInitializedCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + user.NewHumanPasswordChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + false, + "", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -695,13 +668,9 @@ func TestCommandSide_InitCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanInitialCodeSentEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanInitialCodeSentEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index d8aac85e5..103899e58 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -71,7 +71,7 @@ func (c *Commands) createHumanTOTP(ctx context.Context, userID, resourceOwner st human, err := c.getHuman(ctx, userID, resourceOwner) if err != nil { logging.WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname") - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-MM9fs", "Errors.User.NotFound") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-SqyJz", "Errors.User.NotFound") } org, err := c.getOrg(ctx, human.ResourceOwner) if err != nil { diff --git a/internal/command/user_human_otp_test.go b/internal/command/user_human_otp_test.go index ba3a6dc7b..09b97dd95 100644 --- a/internal/command/user_human_otp_test.go +++ b/internal/command/user_human_otp_test.go @@ -18,7 +18,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -84,19 +83,17 @@ func TestCommandSide_AddHumanTOTP(t *testing.T) { eventstore: eventstoreExpect( t, expectFilter( - eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, ), ), expectFilter(), @@ -117,27 +114,23 @@ func TestCommandSide_AddHumanTOTP(t *testing.T) { eventstore: eventstoreExpect( t, expectFilter( - eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, ), ), expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), - &user.NewAggregate("org1", "org1").Aggregate, - "org", - ), + org.NewOrgAddedEvent(context.Background(), + &user.NewAggregate("org1", "org1").Aggregate, + "org", ), ), expectFilter(), @@ -159,54 +152,47 @@ func TestCommandSide_AddHumanTOTP(t *testing.T) { eventstore: eventstoreExpect( t, expectFilter( - eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.German, - domain.GenderUnspecified, - "email@test.ch", - true, - ), + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, ), ), expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), - &user.NewAggregate("org1", "org1").Aggregate, - "org", - ), + org.NewOrgAddedEvent(context.Background(), + &user.NewAggregate("org1", "org1").Aggregate, + "org", ), ), expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), + org.NewDomainPolicyAddedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + true, + true, + true, ), ), expectFilter( - eventFromEventPusher( - user.NewHumanOTPAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }), + user.NewHumanOTPAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), + user.NewHumanOTPVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "agent1", ), - eventFromEventPusher( - user.NewHumanOTPVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "agent1")), ), ), }, @@ -268,7 +254,7 @@ func TestCommands_createHumanTOTP(t *testing.T) { resourceOwner: "org1", userID: "user1", }, - wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-MM9fs", "Errors.User.NotFound"), + wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-SqyJz", "Errors.User.NotFound"), }, { name: "org not existing, not found error", @@ -648,12 +634,10 @@ func TestCommands_HumanCheckMFATOTPSetup(t *testing.T) { ), ), expectPushFailed(io.ErrClosedPipe, - []*repository.Event{eventFromEventPusher( - user.NewHumanOTPVerifiedEvent(ctx, - userAgg, - "agent1", - ), - )}, + user.NewHumanOTPVerifiedEvent(ctx, + userAgg, + "agent1", + ), ), ), }, @@ -674,12 +658,12 @@ func TestCommands_HumanCheckMFATOTPSetup(t *testing.T) { user.NewHumanOTPAddedEvent(ctx, userAgg, secret), ), ), - expectPush([]*repository.Event{eventFromEventPusher( + expectPush( user.NewHumanOTPVerifiedEvent(ctx, userAgg, "agent1", ), - )}), + ), ), }, args: args{ @@ -777,13 +761,9 @@ func TestCommandSide_RemoveHumanTOTP(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanOTPRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -957,13 +937,9 @@ func TestCommandSide_AddHumanOTPSMS(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPSMSAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1032,13 +1008,9 @@ func TestCommandSide_AddHumanOTPSMSWithCheckSucceeded(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPSMSAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1071,27 +1043,21 @@ func TestCommandSide_AddHumanOTPSMSWithCheckSucceeded(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCheckSucceededEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPSMSAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), + user.NewHumanOTPSMSCheckSucceededEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), }, @@ -1211,13 +1177,9 @@ func TestCommandSide_RemoveHumanOTPSMS(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSRemovedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPSMSRemovedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -1339,21 +1301,17 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - nil, - ), - ), - }, + user.NewHumanOTPSMSCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -1383,21 +1341,17 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - nil, - ), - ), - }, + user.NewHumanOTPSMSCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -1439,29 +1393,25 @@ func TestCommandSide_HumanSendOTPSMS(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPSMSCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -1565,13 +1515,9 @@ func TestCommandSide_HumanOTPSMSCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCodeSentEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPSMSCodeSentEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1726,22 +1672,18 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCheckFailedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPSMSCheckFailedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1798,22 +1740,18 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPSMSCheckSucceededEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPSMSCheckSucceededEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -1944,13 +1882,9 @@ func TestCommandSide_AddHumanOTPEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPEmailAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -2019,13 +1953,9 @@ func TestCommandSide_AddHumanOTPEmailWithCheckSucceeded(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPEmailAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -2058,27 +1988,21 @@ func TestCommandSide_AddHumanOTPEmailWithCheckSucceeded(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCheckSucceededEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPEmailAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), + user.NewHumanOTPEmailCheckSucceededEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), }, @@ -2198,13 +2122,9 @@ func TestCommandSide_RemoveHumanOTPEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailRemovedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPEmailRemovedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -2326,21 +2246,17 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - nil, - ), - ), - }, + user.NewHumanOTPEmailCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -2370,21 +2286,17 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - nil, - ), - ), - }, + user.NewHumanOTPEmailCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + nil, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -2426,29 +2338,25 @@ func TestCommandSide_HumanSendOTPEmail(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCodeAddedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPEmailCodeAddedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -2552,13 +2460,9 @@ func TestCommandSide_HumanOTPEmailCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCodeSentEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanOTPEmailCodeSentEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -2713,22 +2617,18 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCheckFailedEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPEmailCheckFailedEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -2785,22 +2685,18 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusherWithInstanceID("inst1", - user.NewHumanOTPEmailCheckSucceededEvent(ctx, - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "authRequestID", - UserAgentID: "userAgentID", - BrowserInfo: &user.BrowserInfo{ - UserAgent: "user-agent", - AcceptLanguage: "en", - RemoteIP: net.IP{192, 0, 2, 1}, - }, - }, - ), - ), - }, + user.NewHumanOTPEmailCheckSucceededEvent(ctx, + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "authRequestID", + UserAgentID: "userAgentID", + BrowserInfo: &user.BrowserInfo{ + UserAgent: "user-agent", + AcceptLanguage: "en", + RemoteIP: net.IP{192, 0, 2, 1}, + }, + }, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/user_human_password.go b/internal/command/user_human_password.go index 0a2992a46..487907212 100644 --- a/internal/command/user_human_password.go +++ b/internal/command/user_human_password.go @@ -175,7 +175,7 @@ func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (err error) { if userID == "" { - return caos_errs.ThrowInvalidArgument(nil, "COMMAND-MM9fs", "Errors.User.UserIDMissing") + return caos_errs.ThrowInvalidArgument(nil, "COMMAND-meEfe", "Errors.User.UserIDMissing") } existingPassword, err := c.passwordWriteModel(ctx, userID, orgID) diff --git a/internal/command/user_human_password_test.go b/internal/command/user_human_password_test.go index 0a59b2ad3..2af86bc72 100644 --- a/internal/command/user_human_password_test.go +++ b/internal/command/user_human_password_test.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -158,16 +157,12 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - true, - "", - ), - ), - }, + user.NewHumanPasswordChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + true, + "", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -225,16 +220,12 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - false, - "", - ), - ), - }, + user.NewHumanPasswordChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + false, + "", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -482,16 +473,12 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - false, - "", - ), - ), - }, + user.NewHumanPasswordChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + false, + "", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -758,16 +745,12 @@ func TestCommandSide_ChangePassword(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password1", - false, - "", - ), - ), - }, + user.NewHumanPasswordChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password1", + false, + "", + ), ), }, res: res{ @@ -929,21 +912,17 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { &user.NewAggregate("user1", "org1").Aggregate)), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - domain.NotificationTypeEmail, - ), - ), - }, + user.NewHumanPasswordCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + domain.NotificationTypeEmail, + ), ), ), }, @@ -1057,13 +1036,9 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCodeSentEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPasswordCodeSentEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1362,17 +1337,13 @@ func TestCommandSide_CheckPassword(t *testing.T) { "")), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCheckFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "agent1", - }, - ), - ), - }, + user.NewHumanPasswordCheckFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "agent1", + }, + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -1449,22 +1420,16 @@ func TestCommandSide_CheckPassword(t *testing.T) { "")), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCheckFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "agent1", - }, - ), - ), - eventFromEventPusher( - user.NewUserLockedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPasswordCheckFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "agent1", + }, + ), + user.NewUserLockedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -1543,17 +1508,13 @@ func TestCommandSide_CheckPassword(t *testing.T) { "")), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "agent1", - }, - ), - ), - }, + user.NewHumanPasswordCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "agent1", + }, + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -1627,23 +1588,18 @@ func TestCommandSide_CheckPassword(t *testing.T) { "")), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "agent1", - }, - ), - ), - eventFromEventPusher( - user.NewHumanPasswordHashUpdatedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - ), - ), - }, + user.NewHumanPasswordCheckSucceededEvent( + context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "agent1", + }, + ), + user.NewHumanPasswordHashUpdatedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), @@ -1727,23 +1683,17 @@ func TestCommandSide_CheckPassword(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "agent1", - }, - ), - ), - eventFromEventPusher( - user.NewHumanPasswordHashUpdatedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "$plain$x$password", - ), - ), - }, + user.NewHumanPasswordCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "agent1", + }, + ), + user.NewHumanPasswordHashUpdatedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "$plain$x$password", + ), ), ), userPasswordHasher: mockPasswordHasher("x"), diff --git a/internal/command/user_human_phone_test.go b/internal/command/user_human_phone_test.go index 8a2b426cb..d018573ee 100644 --- a/internal/command/user_human_phone_test.go +++ b/internal/command/user_human_phone_test.go @@ -13,7 +13,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/user" @@ -152,19 +151,13 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "+41719876543", - ), - ), - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41719876543", + ), + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -218,13 +211,65 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + email: &domain.Phone{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + }, + PhoneNumber: "+41711234567", + IsPhoneVerified: true, + }, + resourceOwner: "org1", + }, + res: res{ + want: &domain.Phone{ + ObjectRoot: models.ObjectRoot{ + AggregateID: "user1", + ResourceOwner: "org1", + }, + PhoneNumber: "+41711234567", + IsPhoneVerified: true, + }, + }, + }, + { + name: "phone changed to verified, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, ), - }, + ), + eventFromEventPusher( + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41711234567", + ), + ), + ), + expectPush( + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -272,26 +317,20 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "+41711234567", - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41711234567", + ), + user.NewHumanPhoneCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), }, @@ -480,13 +519,9 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneVerificationFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneVerificationFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -541,13 +576,9 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -720,20 +751,16 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("12345678"), - }, - time.Hour*1, - ), - ), - }, + user.NewHumanPhoneCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("12345678"), + }, + time.Hour*1, + ), ), ), userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"), @@ -848,13 +875,9 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneCodeSentEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneCodeSentEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -993,13 +1016,9 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_human_profile_test.go b/internal/command/user_human_profile_test.go index 47fd9d122..aea3ef562 100644 --- a/internal/command/user_human_profile_test.go +++ b/internal/command/user_human_profile_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -125,19 +124,15 @@ func TestCommandSide_ChangeHumanProfile(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newProfileChangedEvent(context.Background(), - "user1", "org1", - "firstname2", - "lastname2", - "nickname2", - "displayname2", - language.English, - domain.GenderMale, - ), - ), - }, + newProfileChangedEvent(context.Background(), + "user1", "org1", + "firstname2", + "lastname2", + "nickname2", + "displayname2", + language.English, + domain.GenderMale, + ), ), ), }, diff --git a/internal/command/user_human_refresh_token_test.go b/internal/command/user_human_refresh_token_test.go index 760604c66..6ad7f6865 100644 --- a/internal/command/user_human_refresh_token_test.go +++ b/internal/command/user_human_refresh_token_test.go @@ -14,7 +14,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -376,13 +375,11 @@ func TestCommands_RevokeRefreshToken(t *testing.T) { )), ), expectPushFailed(caos_errs.ThrowInternal(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - )), - }, + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", + ), ), ), }, @@ -417,13 +414,11 @@ func TestCommands_RevokeRefreshToken(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - )), - }, + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", + ), ), ), }, @@ -563,18 +558,16 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) { )), ), expectPushFailed(caos_errs.ThrowInternal(nil, "ERROR", "internal"), - []*repository.Event{ - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - )), - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID2", - )), - }, + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", + ), + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID2", + ), ), ), }, @@ -625,18 +618,16 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) { )), ), expectPush( - []*repository.Event{ - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - )), - eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent( - context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID2", - )), - }, + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", + ), + user.NewHumanRefreshTokenRemovedEvent( + context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID2", + ), ), ), }, diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go index 4b02e6e44..b7ca941ae 100644 --- a/internal/command/user_human_test.go +++ b/internal/command/user_human_test.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -105,7 +104,7 @@ func TestCommandSide_AddHuman(t *testing.T) { eventstore: expectEventstore( expectFilter( eventFromEventPusher( - newAddHumanEvent("$plain$x$password", true, ""), + newAddHumanEvent("$plain$x$password", true, true, ""), ), ), ), @@ -219,35 +218,28 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanAddedEvent(context.Background(), - &userAgg.Aggregate, - "username", - "firstname", - "lastname", - "", - "firstname lastname", - language.English, - domain.GenderUnspecified, - "email@test.ch", - true, - ), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &userAgg.Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("userinit"), - }, - time.Hour*1, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + user.NewHumanAddedEvent(context.Background(), + &userAgg.Aggregate, + "username", + "firstname", + "lastname", + "", + "firstname lastname", + language.English, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &userAgg.Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("userinit"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -306,24 +298,17 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("userinit"), - }, - 1*time.Hour, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, ""), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("userinit"), + }, + 1*time.Hour, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -382,26 +367,19 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("emailCode"), - }, - 1*time.Hour, - "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}", - false, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, ""), + user.NewHumanEmailCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("emailCode"), + }, + 1*time.Hour, + "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}", + false, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -461,26 +439,19 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("emailCode"), - }, - 1*time.Hour, - "", - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, ""), + user.NewHumanEmailCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("emailCode"), + }, + 1*time.Hour, + "", + true, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -541,16 +512,10 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", true, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &userAgg.Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", true, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &userAgg.Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -610,16 +575,10 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", true, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &userAgg.Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", true, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &userAgg.Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -679,16 +638,10 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", true, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &userAgg.Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", false)), + newAddHumanEvent("$plain$x$password", true, false, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &userAgg.Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -808,31 +761,25 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - func() eventstore.Command { - event := user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username@test.ch", - "firstname", - "lastname", - "", - "firstname lastname", - language.English, - domain.GenderUnspecified, - "email@test.ch", - true, - ) - event.AddPasswordData("$plain$x$password", true) - return event - }(), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &userAgg.Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username@test.ch", "org1", false)), + func() eventstore.Command { + event := user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username@test.ch", + "firstname", + "lastname", + "", + "firstname lastname", + language.English, + domain.GenderUnspecified, + "email@test.ch", + false, + ) + event.AddPasswordData("$plain$x$password", true) + return event + }(), + user.NewHumanEmailVerifiedEvent(context.Background(), + &userAgg.Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -893,28 +840,21 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent( - context.Background(), - &userAgg.Aggregate, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &userAgg.Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("phonecode"), - }, - time.Hour*1)), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, "+41711234567"), + user.NewHumanEmailVerifiedEvent( + context.Background(), + &userAgg.Aggregate, + ), + user.NewHumanPhoneCodeAddedEvent(context.Background(), + &userAgg.Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("phonecode"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -965,31 +905,22 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent( - context.Background(), - &userAgg.Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("userinit"), - }, - 1*time.Hour, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent( - context.Background(), - &userAgg.Aggregate, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("", false, true, "+41711234567"), + user.NewHumanInitialCodeAddedEvent( + context.Background(), + &userAgg.Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("userinit"), + }, + 1*time.Hour, + ), + user.NewHumanPhoneVerifiedEvent( + context.Background(), + &userAgg.Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1049,30 +980,21 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEventV2( - context.Background(), - &userAgg.Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("phoneCode"), - }, - 1*time.Hour, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, "+41711234567"), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), + user.NewHumanPhoneCodeAddedEventV2( + context.Background(), + &userAgg.Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("phoneCode"), + }, + 1*time.Hour, + true, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1124,33 +1046,24 @@ func TestCommandSide_AddHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("", false, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent( - context.Background(), - &userAgg.Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("userinit"), - }, - 1*time.Hour, - ), - ), - eventFromEventPusher( - user.NewMetadataSetEvent( - context.Background(), - &userAgg.Aggregate, - "testKey", - []byte("testValue"), - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("", false, true, ""), + user.NewHumanInitialCodeAddedEvent( + context.Background(), + &userAgg.Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("userinit"), + }, + 1*time.Hour, + ), + user.NewMetadataSetEvent( + context.Background(), + &userAgg.Aggregate, + "testKey", + []byte("testValue"), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1200,9 +1113,9 @@ func TestCommandSide_AddHuman(t *testing.T) { if !assert.NoError(t, err) { t.FailNow() } - } - if tt.res.err != nil && !tt.res.err(err) { + } else if !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) + return } if tt.res.err == nil { assert.Equal(t, tt.res.want, tt.args.human.Details) @@ -1399,24 +1312,17 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", true, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", true, true, ""), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1490,16 +1396,10 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1576,29 +1476,20 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "code1", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("", false, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), + user.NewHumanPasswordlessInitCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "code1", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"), @@ -1683,29 +1574,20 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "code1", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), + user.NewHumanPasswordlessInitCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "code1", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"), @@ -1793,34 +1675,26 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1)), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, "+41711234567"), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), + user.NewHumanPhoneCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -1900,28 +1774,19 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newAddHumanEvent("$plain$x$password", false, true, "+41711234567"), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -2059,25 +1924,15 @@ func TestCommandSide_ImportHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newAddHumanEvent("", false, ""), - ), - eventFromEventPusher( - user.NewUserIDPLinkAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "idpID", - "name", - "externalID", - ), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUserIDPLinkUniqueConstraint("idpID", "externalID")), + newAddHumanEvent("", false, true, ""), + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpID", + "name", + "externalID", + ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -2770,24 +2625,17 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("email@test.ch", "$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("email@test.ch", "org1", false)), + newRegisterHumanEvent("email@test.ch", "$plain$x$password", false, false, ""), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -2882,24 +2730,17 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", false)), + newRegisterHumanEvent("username", "$plain$x$password", false, false, ""), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -2995,24 +2836,17 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newRegisterHumanEvent("username", "$plain$x$password", false, true, ""), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -3108,16 +2942,9 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newRegisterHumanEvent("username", "$plain$x$password", false, true, ""), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -3215,34 +3042,27 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1)), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newRegisterHumanEvent("username", "$plain$x$password", false, true, "+41711234567"), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), + user.NewHumanPhoneCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -3344,28 +3164,20 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, "+41711234567"), - ), - eventFromEventPusher( - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + newRegisterHumanEvent("username", "$plain$x$password", false, true, "+41711234567"), + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + ), + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -3525,25 +3337,16 @@ func TestCommandSide_RegisterHuman(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newRegisterHumanEvent("username", "$plain$x$password", false, ""), - ), - eventFromEventPusher( - user.NewUserIDPLinkAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "idpID", - "displayName", - "externalID", - ), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUserIDPLinkUniqueConstraint("idpID", "externalID")), + newRegisterHumanEvent("username", "$plain$x$password", false, true, ""), + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpID", + "displayName", + "externalID", + ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -3692,13 +3495,9 @@ func TestCommandSide_HumanMFASkip(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanMFAInitSkippedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanMFAInitSkippedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -3820,14 +3619,10 @@ func TestCommandSide_HumanSignOut(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanSignedOutEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "agent1", - ), - ), - }, + user.NewHumanSignedOutEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "agent1", + ), ), ), }, @@ -3880,20 +3675,14 @@ func TestCommandSide_HumanSignOut(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanSignedOutEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "agent1", - ), - ), - eventFromEventPusher( - user.NewHumanSignedOutEvent(context.Background(), - &user.NewAggregate("user2", "org1").Aggregate, - "agent1", - ), - ), - }, + user.NewHumanSignedOutEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "agent1", + ), + user.NewHumanSignedOutEvent(context.Background(), + &user.NewAggregate("user2", "org1").Aggregate, + "agent1", + ), ), ), }, @@ -3925,7 +3714,7 @@ func TestCommandSide_HumanSignOut(t *testing.T) { } } -func newAddHumanEvent(password string, changeRequired bool, phone string) *user.HumanAddedEvent { +func newAddHumanEvent(password string, changeRequired, userLoginMustBeDomain bool, phone string) *user.HumanAddedEvent { event := user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, "username", @@ -3936,7 +3725,7 @@ func newAddHumanEvent(password string, changeRequired bool, phone string) *user. language.English, domain.GenderUnspecified, "email@test.ch", - true, + userLoginMustBeDomain, ) if password != "" { event.AddPasswordData(password, changeRequired) @@ -3947,7 +3736,7 @@ func newAddHumanEvent(password string, changeRequired bool, phone string) *user. return event } -func newRegisterHumanEvent(username, password string, changeRequired bool, phone string) *user.HumanRegisteredEvent { +func newRegisterHumanEvent(username, password string, changeRequired, userLoginMustBeUnique bool, phone string) *user.HumanRegisteredEvent { event := user.NewHumanRegisteredEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, username, @@ -3958,7 +3747,7 @@ func newRegisterHumanEvent(username, password string, changeRequired bool, phone language.Und, domain.GenderUnspecified, "email@test.ch", - true, + userLoginMustBeUnique, ) if password != "" { event.AddPasswordData(password, changeRequired) diff --git a/internal/command/user_idp_link.go b/internal/command/user_idp_link.go index bf7b6c72c..a66140ec7 100644 --- a/internal/command/user_idp_link.go +++ b/internal/command/user_idp_link.go @@ -34,7 +34,7 @@ func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner str } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/user_idp_link_test.go b/internal/command/user_idp_link_test.go index 68d7611c5..f86ab12db 100644 --- a/internal/command/user_idp_link_test.go +++ b/internal/command/user_idp_link_test.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/instance" @@ -381,17 +380,12 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserIDPLinkAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "config1", - "name", - "externaluser1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUserIDPLinkUniqueConstraint("config1", "externaluser1")), + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "config1", + "name", + "externaluser1", + ), ), ), }, @@ -485,17 +479,12 @@ func TestCommandSide_BulkAddUserIDPLinks(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserIDPLinkAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "config1", - "name", - "externaluser1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUserIDPLinkUniqueConstraint("config1", "externaluser1")), + user.NewUserIDPLinkAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "config1", + "name", + "externaluser1", + ), ), ), }, @@ -662,16 +651,11 @@ func TestCommandSide_RemoveUserIDPLink(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserIDPLinkRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "config1", - "externaluser1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("config1", "externaluser1")), + user.NewUserIDPLinkRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "config1", + "externaluser1", + ), ), ), }, @@ -802,18 +786,14 @@ func TestCommandSide_ExternalLoginCheck(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserIDPCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &user.AuthRequestInfo{ - ID: "request1", - UserAgentID: "useragent1", - SelectedIDPConfigID: "config1", - }, - ), - ), - }, + user.NewUserIDPCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &user.AuthRequestInfo{ + ID: "request1", + UserAgentID: "useragent1", + SelectedIDPConfigID: "config1", + }, + ), ), ), }, @@ -927,16 +907,12 @@ func TestCommandSide_MigrateUserIDP(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserIDPExternalIDMigratedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "idpConfig1", - "previousID", - "newID", - ), - ), - }, + user.NewUserIDPExternalIDMigratedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "idpConfig1", + "previousID", + "newID", + ), ), ), }, diff --git a/internal/command/user_machine.go b/internal/command/user_machine.go index 4a6ebe89c..700964f6b 100644 --- a/internal/command/user_machine.go +++ b/internal/command/user_machine.go @@ -85,7 +85,7 @@ func (c *Commands) AddMachine(ctx context.Context, machine *Machine) (*domain.Ob return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } @@ -104,7 +104,7 @@ func (c *Commands) ChangeMachine(ctx context.Context, machine *Machine) (*domain return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/user_machine_key.go b/internal/command/user_machine_key.go index abf8cd01e..a3535770d 100644 --- a/internal/command/user_machine_key.go +++ b/internal/command/user_machine_key.go @@ -109,7 +109,7 @@ func (c *Commands) AddUserMachineKey(ctx context.Context, machineKey *MachineKey } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } @@ -161,7 +161,7 @@ func (c *Commands) RemoveUserMachineKey(ctx context.Context, machineKey *Machine } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/user_machine_key_test.go b/internal/command/user_machine_key_test.go index b2611ad33..94398fa5c 100644 --- a/internal/command/user_machine_key_test.go +++ b/internal/command/user_machine_key_test.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -141,17 +140,13 @@ func TestCommands_AddMachineKey(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineKeyAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key1", - domain.AuthNKeyTypeJSON, - time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - []byte("public"), - ), - ), - }, + user.NewMachineKeyAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key1", + domain.AuthNKeyTypeJSON, + time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), + []byte("public"), + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"), @@ -194,17 +189,13 @@ func TestCommands_AddMachineKey(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineKeyAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key1", - domain.AuthNKeyTypeJSON, - time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - []byte("public"), - ), - ), - }, + user.NewMachineKeyAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key1", + domain.AuthNKeyTypeJSON, + time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), + []byte("public"), + ), ), ), keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), diff --git a/internal/command/user_machine_secret.go b/internal/command/user_machine_secret.go index 8f28b5e5d..ebec62d85 100644 --- a/internal/command/user_machine_secret.go +++ b/internal/command/user_machine_secret.go @@ -31,7 +31,7 @@ func (c *Commands) GenerateMachineSecret(ctx context.Context, userID string, res return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } @@ -80,7 +80,7 @@ func (c *Commands) RemoveMachineSecret(ctx context.Context, userID string, resou return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } @@ -131,7 +131,7 @@ func (c *Commands) VerifyMachineSecret(ctx context.Context, userID string, resou return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/user_machine_secret_test.go b/internal/command/user_machine_secret_test.go index 42df770d9..77343c8fb 100644 --- a/internal/command/user_machine_secret_test.go +++ b/internal/command/user_machine_secret_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -109,19 +108,15 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineSecretSetEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - ), - ), - }, + user.NewMachineSecretSetEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + ), ), ), }, @@ -287,13 +282,9 @@ func TestCommandSide_RemoveMachineSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineSecretRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewMachineSecretRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -454,13 +445,9 @@ func TestCommandSide_VerifyMachineSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineSecretCheckSucceededEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewMachineSecretCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -505,13 +492,9 @@ func TestCommandSide_VerifyMachineSecret(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineSecretCheckFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewMachineSecretCheckFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_machine_test.go b/internal/command/user_machine_test.go index 2960ab95b..9c91c17cc 100644 --- a/internal/command/user_machine_test.go +++ b/internal/command/user_machine_test.go @@ -9,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" @@ -120,19 +119,14 @@ func TestCommandSide_AddMachine(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMachineAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "name", - "description", - true, - domain.OIDCTokenTypeBearer, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)), + user.NewMachineAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "name", + "description", + true, + domain.OIDCTokenTypeBearer, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), @@ -309,11 +303,7 @@ func TestCommandSide_ChangeMachine(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - newMachineChangedEvent(context.Background(), "user1", "org1", "name1", "description1"), - ), - }, + newMachineChangedEvent(context.Background(), "user1", "org1", "name1", "description1"), ), ), }, diff --git a/internal/command/user_metadata_test.go b/internal/command/user_metadata_test.go index 2d480dab9..f2ef90cf6 100644 --- a/internal/command/user_metadata_test.go +++ b/internal/command/user_metadata_test.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -115,15 +114,11 @@ func TestCommandSide_SetUserMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMetadataSetEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key", - []byte("value"), - ), - ), - }, + user.NewMetadataSetEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key", + []byte("value"), + ), ), ), }, @@ -285,22 +280,16 @@ func TestCommandSide_BulkSetUserMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMetadataSetEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key", - []byte("value"), - ), - ), - eventFromEventPusher( - user.NewMetadataSetEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key1", - []byte("value1"), - ), - ), - }, + user.NewMetadataSetEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key", + []byte("value"), + ), + user.NewMetadataSetEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key1", + []byte("value1"), + ), ), ), }, @@ -461,14 +450,10 @@ func TestCommandSide_UserRemoveMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMetadataRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key", - ), - ), - }, + user.NewMetadataRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key", + ), ), ), }, @@ -689,20 +674,14 @@ func TestCommandSide_BulkRemoveUserMetadata(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewMetadataRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key", - ), - ), - eventFromEventPusher( - user.NewMetadataRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "key1", - ), - ), - }, + user.NewMetadataRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key", + ), + user.NewMetadataRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "key1", + ), ), ), }, diff --git a/internal/command/user_personal_access_token.go b/internal/command/user_personal_access_token.go index 4f252c08c..fb8d01c82 100644 --- a/internal/command/user_personal_access_token.go +++ b/internal/command/user_personal_access_token.go @@ -95,7 +95,7 @@ func (c *Commands) AddPersonalAccessToken(ctx context.Context, pat *PersonalAcce } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } @@ -144,7 +144,7 @@ func (c *Commands) RemovePersonalAccessToken(ctx context.Context, pat *PersonalA } return &domain.ObjectDetails{ Sequence: events[len(events)-1].Sequence(), - EventDate: events[len(events)-1].CreationDate(), + EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, }, nil } diff --git a/internal/command/user_personal_access_token_test.go b/internal/command/user_personal_access_token_test.go index 93c2ef576..bfaf5a0b6 100644 --- a/internal/command/user_personal_access_token_test.go +++ b/internal/command/user_personal_access_token_test.go @@ -9,20 +9,14 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/zitadel/zitadel/internal/eventstore/repository" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - - id_mock "github.com/zitadel/zitadel/internal/id/mock" - - "github.com/zitadel/zitadel/internal/repository/user" - - caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" + caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" + id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/user" ) func TestCommands_AddPersonalAccessToken(t *testing.T) { @@ -182,16 +176,12 @@ func TestCommands_AddPersonalAccessToken(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewPersonalAccessTokenAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "token1", - time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - []string{"openid"}, - ), - ), - }, + user.NewPersonalAccessTokenAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "token1", + time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), + []string{"openid"}, + ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "token1"), @@ -234,16 +224,12 @@ func TestCommands_AddPersonalAccessToken(t *testing.T) { ), expectFilter(), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewPersonalAccessTokenAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "token1", - time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - []string{"openid"}, - ), - ), - }, + user.NewPersonalAccessTokenAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "token1", + time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), + []string{"openid"}, + ), ), ), keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), @@ -345,14 +331,10 @@ func TestCommands_RemovePersonalAccessToken(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewPersonalAccessTokenRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "token1", - ), - ), - }, + user.NewPersonalAccessTokenRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "token1", + ), ), ), }, diff --git a/internal/command/user_test.go b/internal/command/user_test.go index a6f16ee32..7073071cf 100644 --- a/internal/command/user_test.go +++ b/internal/command/user_test.go @@ -12,10 +12,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" "github.com/zitadel/zitadel/internal/repository/instance" - "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" @@ -299,18 +297,12 @@ func TestCommandSide_UsernameChange(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "test@test.ch", - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("test@test.ch", "org1", true)), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "test@test.ch", + true, + ), ), ), }, @@ -366,18 +358,12 @@ func TestCommandSide_UsernameChange(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "test@test.ch", - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", false)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("test@test.ch", "org1", false)), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "test@test.ch", + false, + ), ), ), }, @@ -425,18 +411,12 @@ func TestCommandSide_UsernameChange(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "username1", - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username1", "org1", true)), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "username1", + true, + ), ), ), }, @@ -484,18 +464,12 @@ func TestCommandSide_UsernameChange(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUsernameChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - "username1", - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username1", "org1", true)), + user.NewUsernameChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "username1", + true, + ), ), ), }, @@ -644,13 +618,9 @@ func TestCommandSide_DeactivateUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserDeactivatedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewUserDeactivatedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -797,13 +767,9 @@ func TestCommandSide_ReactivateUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserReactivatedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewUserReactivatedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -951,13 +917,9 @@ func TestCommandSide_LockUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserLockedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewUserLockedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1104,13 +1066,9 @@ func TestCommandSide_UnlockUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserUnlockedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewUserUnlockedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1269,17 +1227,12 @@ func TestCommandSide_RemoveUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - nil, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), + user.NewUserRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + nil, + true, + ), ), ), }, @@ -1335,18 +1288,17 @@ func TestCommandSide_RemoveUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - nil, - true, - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(user.NewRemoveUserIDPLinkUniqueConstraint("idpConfigID", "externalUserID")), + user.NewUserRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + []*domain.UserIDPLink{ + { + IDPConfigID: "idpConfigID", + ExternalUserID: "externalUserID", + }, + }, + true, + ), ), ), }, @@ -1394,46 +1346,29 @@ func TestCommandSide_RemoveUser(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewUserRemovedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "username", - nil, - true, - ), - ), - eventFromEventPusher( - instance.NewMemberCascadeRemovedEvent(context.Background(), - &instance.NewAggregate("INSTANCE").Aggregate, - "user1", - ), - ), - eventFromEventPusher( - org.NewMemberCascadeRemovedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "user1", - ), - ), - eventFromEventPusher( - project.NewProjectMemberCascadeRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - ), - ), - eventFromEventPusher( - project.NewProjectGrantMemberCascadeRemovedEvent(context.Background(), - &project.NewAggregate("project1", "org1").Aggregate, - "user1", - "grant1", - ), - ), - }, - uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("INSTANCE", "user1")), - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("org1", "user1")), - uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")), - uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantMemberUniqueConstraint("project1", "user1", "grant1")), + user.NewUserRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + nil, + true, + ), + instance.NewMemberCascadeRemovedEvent(context.Background(), + &instance.NewAggregate("INSTANCE").Aggregate, + "user1", + ), + org.NewMemberCascadeRemovedEvent(context.Background(), + &org.NewAggregate("org1").Aggregate, + "user1", + ), + project.NewProjectMemberCascadeRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + ), + project.NewProjectGrantMemberCascadeRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + "grant1", + ), ), ), }, @@ -1489,13 +1424,11 @@ func TestCommandSide_RemoveUser(t *testing.T) { got, err := r.RemoveUser(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.cascadeUserMemberships, tt.args.cascadeUserGrants...) if tt.res.err == nil { assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { + } else if !tt.res.err(err) { t.Errorf("got wrong err: %v ", err) + return } - if tt.res.err == nil { - assert.Equal(t, tt.res.want, got) - } + assert.Equal(t, tt.res.want, got) }) } } @@ -1669,11 +1602,9 @@ func TestCommands_RevokeAccessToken(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - user.NewUserTokenRemovedEvent(context.Background(), - &user.NewAggregate("userID", "orgID").Aggregate, - "tokenID", - ), + user.NewUserTokenRemovedEvent(context.Background(), + &user.NewAggregate("userID", "orgID").Aggregate, + "tokenID", ), ), ), @@ -1783,13 +1714,9 @@ func TestCommandSide_UserDomainClaimedSent(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewDomainClaimedSentEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewDomainClaimedSentEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_v2_email_test.go b/internal/command/user_v2_email_test.go index dfcbe9107..ec8b7f755 100644 --- a/internal/command/user_v2_email_test.go +++ b/internal/command/user_v2_email_test.go @@ -15,7 +15,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/user" @@ -521,19 +520,13 @@ func TestCommands_ChangeUserEmailVerified(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -561,7 +554,7 @@ func TestCommands_ChangeUserEmailVerified(t *testing.T) { } got, err := c.ChangeUserEmailVerified(context.Background(), tt.args.userID, tt.args.resourceOwner, tt.args.email) require.ErrorIs(t, err, tt.wantErr) - assert.Equal(t, got, tt.want) + assert.Equal(t, tt.want, got) }) } } @@ -720,27 +713,21 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - "", false, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "", false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -783,27 +770,21 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - "", true, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "", true, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -847,27 +828,21 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "email-changed@test.ch", - ), - ), - eventFromEventPusher( - user.NewHumanEmailCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", false, - ), - ), - }, + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email-changed@test.ch", + ), + user.NewHumanEmailCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -1023,13 +998,9 @@ func TestCommands_VerifyUserEmail(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerificationFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailVerificationFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1146,13 +1117,9 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerificationFailedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailVerificationFailedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, @@ -1198,13 +1165,9 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), }, diff --git a/internal/command/user_v2_passkey_test.go b/internal/command/user_v2_passkey_test.go index 4d87e4bf4..2a735dab8 100644 --- a/internal/command/user_v2_passkey_test.go +++ b/internal/command/user_v2_passkey_test.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/org" @@ -175,9 +174,9 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) { ), ), expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectPush([]*repository.Event{eventFromEventPusher( + expectPush( user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), - )}), + ), ), }, args: args{ @@ -286,9 +285,9 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) { ), ), expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))), - expectPush([]*repository.Event{eventFromEventPusher( + expectPush( user.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, "123"), - )}), + ), ), }, args: args{ @@ -394,11 +393,11 @@ func TestCommands_pushUserPasskey(t *testing.T) { { name: "push error", expectPush: func(challenge string) expect { - return expectPushFailed(io.ErrClosedPipe, []*repository.Event{eventFromEventPusher( + return expectPushFailed(io.ErrClosedPipe, user.NewHumanPasswordlessAddedEvent(ctx, userAgg, "123", challenge, "rpID", ), - )}) + ) }, args: args{}, wantErr: io.ErrClosedPipe, @@ -406,27 +405,23 @@ func TestCommands_pushUserPasskey(t *testing.T) { { name: "success", expectPush: func(challenge string) expect { - return expectPush([]*repository.Event{eventFromEventPusher( + return expectPush( user.NewHumanPasswordlessAddedEvent(ctx, userAgg, "123", challenge, "rpID", ), - )}) + ) }, args: args{}, }, { name: "initcode succeeded event", expectPush: func(challenge string) expect { - return expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessAddedEvent(ctx, - userAgg, "123", challenge, "rpID", - ), + return expectPush( + user.NewHumanPasswordlessAddedEvent(ctx, + userAgg, "123", challenge, "rpID", ), - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), - ), - }) + user.NewHumanPasswordlessInitCodeCheckSucceededEvent(ctx, userAgg, "123"), + ) }, args: args{ events: []eventCallback{func(ctx context.Context, userAgg *eventstore.Aggregate) eventstore.Command { @@ -509,19 +504,17 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) { true, ), )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), + expectPush( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, ), - }), + ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), }, @@ -615,22 +608,20 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) { true, ), )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, - time.Minute, - "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", - false, - ), + expectPush( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, + time.Minute, + "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}", + false, ), - }), + ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), }, @@ -709,19 +700,17 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) { true, ), )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", true, - ), + expectPush( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", true, ), - }), + ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), }, @@ -832,19 +821,17 @@ func TestCommands_addUserPasskeyCode(t *testing.T) { true, ), )), - expectPushFailed(io.ErrClosedPipe, []*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), + expectPushFailed(io.ErrClosedPipe, + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, ), - }), + ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), }, @@ -873,19 +860,17 @@ func TestCommands_addUserPasskeyCode(t *testing.T) { true, ), )), - expectPush([]*repository.Event{ - eventFromEventPusher( - user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), - userAgg, - "123", &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("passkey1"), - }, time.Minute, "", false, - ), + expectPush( + user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(), + userAgg, + "123", &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("passkey1"), + }, time.Minute, "", false, ), - }), + ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"), }, diff --git a/internal/command/user_v2_password_test.go b/internal/command/user_v2_password_test.go index ecf21ff18..2465c240f 100644 --- a/internal/command/user_v2_password_test.go +++ b/internal/command/user_v2_password_test.go @@ -437,19 +437,18 @@ func TestCommands_requestPasswordReset(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("code"), - }, - 10*time.Minute, - domain.NotificationTypeEmail, - "", - false, - )), + user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("code"), + }, + 10*time.Minute, + domain.NotificationTypeEmail, + "", + false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -478,19 +477,18 @@ func TestCommands_requestPasswordReset(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("code"), - }, - 10*time.Minute, - domain.NotificationTypeEmail, - "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", - false, - )), + user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("code"), + }, + 10*time.Minute, + domain.NotificationTypeEmail, + "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -520,19 +518,18 @@ func TestCommands_requestPasswordReset(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("code"), - }, - 10*time.Minute, - domain.NotificationTypeSms, - "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", - false, - )), + user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("code"), + }, + 10*time.Minute, + domain.NotificationTypeSms, + "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -563,19 +560,18 @@ func TestCommands_requestPasswordReset(t *testing.T) { ), ), expectPush( - eventPusherToEvents( - user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("code"), - }, - 10*time.Minute, - domain.NotificationTypeEmail, - "", - true, - )), + user.NewHumanPasswordCodeAddedEventV2(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("code"), + }, + 10*time.Minute, + domain.NotificationTypeEmail, + "", + true, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), diff --git a/internal/command/user_v2_phone_test.go b/internal/command/user_v2_phone_test.go index 05da4e260..3a4bab096 100644 --- a/internal/command/user_v2_phone_test.go +++ b/internal/command/user_v2_phone_test.go @@ -15,7 +15,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/user" @@ -427,19 +426,13 @@ func TestCommands_ChangeUserPhoneVerified(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "+41791234568", - ), - ), - eventFromEventPusher( - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - }, + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41791234568", + ), + user.NewHumanPhoneVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -637,27 +630,21 @@ func TestCommands_changeUserPhoneWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "+41791234568", - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - false, - ), - ), - }, + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41791234568", + ), + user.NewHumanPhoneCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + false, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), @@ -703,27 +690,21 @@ func TestCommands_changeUserPhoneWithGenerator(t *testing.T) { ), ), expectPush( - []*repository.Event{ - eventFromEventPusher( - user.NewHumanPhoneChangedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "+41791234568", - ), - ), - eventFromEventPusher( - user.NewHumanPhoneCodeAddedEventV2(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - true, - ), - ), - }, + user.NewHumanPhoneChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "+41791234568", + ), + user.NewHumanPhoneCodeAddedEventV2(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + true, + ), ), ), checkPermission: newMockPermissionCheckAllowed(), diff --git a/internal/command/user_v2_totp_test.go b/internal/command/user_v2_totp_test.go index 32a5d9e09..33c7dc103 100644 --- a/internal/command/user_v2_totp_test.go +++ b/internal/command/user_v2_totp_test.go @@ -16,7 +16,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -59,7 +58,7 @@ func TestCommands_AddUserTOTP(t *testing.T) { expectFilter(), ), }, - wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-MM9fs", "Errors.User.NotFound"), + wantErr: caos_errs.ThrowPreconditionFailed(nil, "COMMAND-SqyJz", "Errors.User.NotFound"), }, { name: "push error", @@ -105,9 +104,9 @@ func TestCommands_AddUserTOTP(t *testing.T) { ), ), expectFilter(), - expectRandomPushFailed(io.ErrClosedPipe, []*repository.Event{eventFromEventPusher( + expectRandomPushFailed(io.ErrClosedPipe, []eventstore.Command{ user.NewHumanOTPAddedEvent(ctx, userAgg, nil), - )}), + }), ), }, wantErr: io.ErrClosedPipe, @@ -156,9 +155,9 @@ func TestCommands_AddUserTOTP(t *testing.T) { ), ), expectFilter(), - expectRandomPush([]*repository.Event{eventFromEventPusher( + expectRandomPush([]eventstore.Command{ user.NewHumanOTPAddedEvent(ctx, userAgg, nil), - )}), + }), ), }, want: true, @@ -230,9 +229,9 @@ func TestCommands_CheckUserTOTP(t *testing.T) { user.NewHumanOTPAddedEvent(ctx, userAgg, secret), ), ), - expectPush([]*repository.Event{eventFromEventPusher( + expectPush( user.NewHumanOTPVerifiedEvent(ctx, userAgg, ""), - )}), + ), ), }, args: args{ diff --git a/internal/command/user_v2_u2f_test.go b/internal/command/user_v2_u2f_test.go index 6ae5bf6e2..9eb850004 100644 --- a/internal/command/user_v2_u2f_test.go +++ b/internal/command/user_v2_u2f_test.go @@ -13,7 +13,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" "github.com/zitadel/zitadel/internal/repository/org" @@ -173,22 +172,22 @@ func TestCommands_pushUserU2F(t *testing.T) { { name: "push error", expectPush: func(challenge string) expect { - return expectPushFailed(io.ErrClosedPipe, []*repository.Event{eventFromEventPusher( + return expectPushFailed(io.ErrClosedPipe, user.NewHumanU2FAddedEvent(ctx, userAgg, "123", challenge, "rpID", ), - )}) + ) }, wantErr: io.ErrClosedPipe, }, { name: "success", expectPush: func(challenge string) expect { - return expectPush([]*repository.Event{eventFromEventPusher( + return expectPush( user.NewHumanU2FAddedEvent(ctx, userAgg, "123", challenge, "rpID", ), - )}) + ) }, }, } diff --git a/internal/database/cockroach/config.go b/internal/database/cockroach/config.go deleted file mode 100644 index ca51af16d..000000000 --- a/internal/database/cockroach/config.go +++ /dev/null @@ -1,169 +0,0 @@ -package cockroach - -import ( - "database/sql" - "strconv" - "strings" - "time" - - "github.com/mitchellh/mapstructure" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/database/dialect" -) - -const ( - sslDisabledMode = "disable" - sslRequireMode = "require" - sslAllowMode = "allow" - sslPreferMode = "prefer" -) - -type Config struct { - Host string - Port uint16 - Database string - MaxOpenConns uint32 - MaxIdleConns uint32 - MaxConnLifetime time.Duration - MaxConnIdleTime time.Duration - User User - Admin User - - //Additional options to be appended as options= - //The value will be taken as is. Multiple options are space separated. - Options string -} - -func (c *Config) MatchName(name string) bool { - for _, key := range []string{"crdb", "cockroach"} { - if strings.TrimSpace(strings.ToLower(name)) == key { - return true - } - } - return false -} - -func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), - WeaklyTypedInput: true, - Result: c, - }) - if err != nil { - return nil, err - } - - for _, config := range configs { - if err = decoder.Decode(config); err != nil { - return nil, err - } - } - return c, nil -} - -func (c *Config) Connect(useAdmin bool) (*sql.DB, error) { - client, err := sql.Open("pgx", c.String(useAdmin)) - if err != nil { - return nil, err - } - - client.SetMaxOpenConns(int(c.MaxOpenConns)) - client.SetMaxIdleConns(int(c.MaxIdleConns)) - client.SetConnMaxLifetime(c.MaxConnLifetime) - client.SetConnMaxIdleTime(c.MaxConnIdleTime) - - return client, nil -} - -func (c *Config) DatabaseName() string { - return c.Database -} - -func (c *Config) Username() string { - return c.User.Username -} - -func (c *Config) Password() string { - return c.User.Password -} - -func (c *Config) Type() string { - return "cockroach" -} - -func (c *Config) Timetravel(d time.Duration) string { - return "" -} - -type User struct { - Username string - Password string - SSL SSL -} - -type SSL struct { - // type of connection security - Mode string - // RootCert Path to the CA certificate - RootCert string - // Cert Path to the client certificate - Cert string - // Key Path to the client private key - Key string -} - -func (c *Config) checkSSL(user User) { - if user.SSL.Mode == sslDisabledMode || user.SSL.Mode == "" { - user.SSL = SSL{Mode: sslDisabledMode} - return - } - - if user.SSL.Mode == sslRequireMode || user.SSL.Mode == sslAllowMode || user.SSL.Mode == sslPreferMode { - return - } - - if user.SSL.RootCert == "" { - logging.WithFields( - "cert set", user.SSL.Cert != "", - "key set", user.SSL.Key != "", - "rootCert set", user.SSL.RootCert != "", - ).Fatal("at least ssl root cert has to be set") - } -} - -func (c Config) String(useAdmin bool) string { - user := c.User - if useAdmin { - user = c.Admin - } - c.checkSSL(user) - fields := []string{ - "host=" + c.Host, - "port=" + strconv.Itoa(int(c.Port)), - "user=" + user.Username, - "dbname=" + c.Database, - "application_name=zitadel", - "sslmode=" + user.SSL.Mode, - } - if c.Options != "" { - fields = append(fields, "options="+c.Options) - } - if !useAdmin { - fields = append(fields, "dbname="+c.Database) - } - if user.Password != "" { - fields = append(fields, "password="+user.Password) - } - if user.SSL.Mode != sslDisabledMode { - fields = append(fields, "sslrootcert="+user.SSL.RootCert) - if user.SSL.Cert != "" { - fields = append(fields, "sslcert="+user.SSL.Cert) - } - if user.SSL.Key != "" { - fields = append(fields, "sslkey="+user.SSL.Key) - } - } - - return strings.Join(fields, " ") -} diff --git a/internal/database/cockroach/crdb.go b/internal/database/cockroach/crdb.go index da634c9c8..7f43bb109 100644 --- a/internal/database/cockroach/crdb.go +++ b/internal/database/cockroach/crdb.go @@ -1,6 +1,15 @@ package cockroach import ( + "database/sql" + "strconv" + "strings" + "time" + + _ "github.com/jackc/pgx/v4/stdlib" + "github.com/mitchellh/mapstructure" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/database/dialect" ) @@ -8,3 +17,173 @@ func init() { config := &Config{} dialect.Register(config, config, true) } + +const ( + sslDisabledMode = "disable" + sslRequireMode = "require" + sslAllowMode = "allow" + sslPreferMode = "prefer" +) + +type Config struct { + Host string + Port uint16 + Database string + MaxOpenConns uint32 + MaxIdleConns uint32 + MaxConnLifetime time.Duration + MaxConnIdleTime time.Duration + User User + Admin User + // Additional options to be appended as options= + // The value will be taken as is. Multiple options are space separated. + Options string +} + +func (c *Config) MatchName(name string) bool { + for _, key := range []string{"crdb", "cockroach"} { + if strings.TrimSpace(strings.ToLower(name)) == key { + return true + } + } + return false +} + +func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + WeaklyTypedInput: true, + Result: c, + }) + if err != nil { + return nil, err + } + + for _, config := range configs { + if err = decoder.Decode(config); err != nil { + return nil, err + } + } + + return c, nil +} + +func (c *Config) Connect(useAdmin, isEventPusher bool, pusherRatio float32, appName string) (*sql.DB, error) { + client, err := sql.Open("pgx", c.String(useAdmin, appName)) + if err != nil { + return nil, err + } + + connInfo, err := dialect.NewConnectionInfo(c.MaxOpenConns, c.MaxIdleConns, float64(pusherRatio)) + if err != nil { + return nil, err + } + + var maxConns, maxIdleConns uint32 + if isEventPusher { + maxConns = connInfo.EventstorePusher.MaxOpenConns + maxIdleConns = connInfo.EventstorePusher.MaxIdleConns + } else { + maxConns = connInfo.ZITADEL.MaxOpenConns + maxIdleConns = connInfo.ZITADEL.MaxIdleConns + } + + client.SetMaxOpenConns(int(maxConns)) + client.SetMaxIdleConns(int(maxIdleConns)) + client.SetConnMaxLifetime(c.MaxConnLifetime) + client.SetConnMaxIdleTime(c.MaxConnIdleTime) + + return client, nil +} + +func (c *Config) DatabaseName() string { + return c.Database +} + +func (c *Config) Username() string { + return c.User.Username +} + +func (c *Config) Password() string { + return c.User.Password +} + +func (c *Config) Type() string { + return "cockroach" +} + +func (c *Config) Timetravel(d time.Duration) string { + return "" +} + +type User struct { + Username string + Password string + SSL SSL +} + +type SSL struct { + // type of connection security + Mode string + // RootCert Path to the CA certificate + RootCert string + // Cert Path to the client certificate + Cert string + // Key Path to the client private key + Key string +} + +func (c *Config) checkSSL(user User) { + if user.SSL.Mode == sslDisabledMode || user.SSL.Mode == "" { + user.SSL = SSL{Mode: sslDisabledMode} + return + } + + if user.SSL.Mode == sslRequireMode || user.SSL.Mode == sslAllowMode || user.SSL.Mode == sslPreferMode { + return + } + + if user.SSL.RootCert == "" { + logging.WithFields( + "cert set", user.SSL.Cert != "", + "key set", user.SSL.Key != "", + "rootCert set", user.SSL.RootCert != "", + ).Fatal("at least ssl root cert has to be set") + } +} + +func (c Config) String(useAdmin bool, appName string) string { + user := c.User + if useAdmin { + user = c.Admin + } + c.checkSSL(user) + fields := []string{ + "host=" + c.Host, + "port=" + strconv.Itoa(int(c.Port)), + "user=" + user.Username, + "dbname=" + c.Database, + "application_name=" + appName, + "sslmode=" + user.SSL.Mode, + } + if c.Options != "" { + fields = append(fields, "options="+c.Options) + } + if !useAdmin { + fields = append(fields, "dbname="+c.Database) + } + if user.Password != "" { + fields = append(fields, "password="+user.Password) + } + if user.SSL.Mode != sslDisabledMode { + fields = append(fields, "sslrootcert="+user.SSL.RootCert) + if user.SSL.Cert != "" { + fields = append(fields, "sslcert="+user.SSL.Cert) + } + if user.SSL.Key != "" { + fields = append(fields, "sslkey="+user.SSL.Key) + } + } + + return strings.Join(fields, " ") +} diff --git a/internal/database/database.go b/internal/database/database.go index 5f4de0122..c889a8842 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -5,6 +5,7 @@ import ( "database/sql" "reflect" + "github.com/mitchellh/mapstructure" "github.com/zitadel/logging" _ "github.com/zitadel/zitadel/internal/database/cockroach" @@ -14,8 +15,9 @@ import ( ) type Config struct { - Dialects map[string]interface{} `mapstructure:",remain"` - connector dialect.Connector + Dialects map[string]interface{} `mapstructure:",remain"` + EventPushConnRatio float32 + connector dialect.Connector } func (c *Config) SetConnector(connector dialect.Connector) { @@ -87,8 +89,18 @@ func (db *DB) QueryRowContext(ctx context.Context, scan func(row *sql.Row) error return row.Err() } -func Connect(config Config, useAdmin bool) (*DB, error) { - client, err := config.connector.Connect(useAdmin) +const ( + zitadelAppName = "zitadel" + EventstorePusherAppName = "zitadel_es_pusher" +) + +func Connect(config Config, useAdmin, isEventPusher bool) (*DB, error) { + appName := zitadelAppName + if isEventPusher { + appName = EventstorePusherAppName + } + + client, err := config.connector.Connect(useAdmin, isEventPusher, config.EventPushConnRatio, appName) if err != nil { return nil, err } @@ -103,20 +115,20 @@ func Connect(config Config, useAdmin bool) (*DB, error) { }, nil } -func DecodeHook(from, to reflect.Value) (interface{}, error) { +func DecodeHook(from, to reflect.Value) (_ interface{}, err error) { if to.Type() != reflect.TypeOf(Config{}) { return from.Interface(), nil } - configuredDialects, ok := from.Interface().(map[string]interface{}) - if !ok { - return from.Interface(), nil + config := new(Config) + if err = mapstructure.Decode(from.Interface(), config); err != nil { + return nil, err } - configuredDialect := dialect.SelectByConfig(configuredDialects) - configs := make([]interface{}, 0, len(configuredDialects)-1) + configuredDialect := dialect.SelectByConfig(config.Dialects) + configs := make([]interface{}, 0, len(config.Dialects)-1) - for name, dialectConfig := range configuredDialects { + for name, dialectConfig := range config.Dialects { if !configuredDialect.Matcher.MatchName(name) { continue } @@ -124,12 +136,12 @@ func DecodeHook(from, to reflect.Value) (interface{}, error) { configs = append(configs, dialectConfig) } - connector, err := configuredDialect.Matcher.Decode(configs) + config.connector, err = configuredDialect.Matcher.Decode(configs) if err != nil { return nil, err } - return Config{connector: connector}, nil + return config, nil } func (c Config) DatabaseName() string { diff --git a/internal/database/dialect/config.go b/internal/database/dialect/config.go index 9e3680710..210e7126d 100644 --- a/internal/database/dialect/config.go +++ b/internal/database/dialect/config.go @@ -6,11 +6,6 @@ import ( "time" ) -type Config struct { - Dialects map[string]interface{} `mapstructure:",remain"` - Dialect Matcher -} - type Dialect struct { Matcher Matcher Config Connector @@ -29,7 +24,7 @@ type Matcher interface { } type Connector interface { - Connect(useAdmin bool) (*sql.DB, error) + Connect(useAdmin, isEventPusher bool, pusherRatio float32, appName string) (*sql.DB, error) Password() string Database } diff --git a/internal/database/dialect/connections.go b/internal/database/dialect/connections.go new file mode 100644 index 000000000..bf3a4680f --- /dev/null +++ b/internal/database/dialect/connections.go @@ -0,0 +1,39 @@ +package dialect + +import "errors" + +type ConnectionInfo struct { + EventstorePusher ConnectionConfig + ZITADEL ConnectionConfig +} + +type ConnectionConfig struct { + MaxOpenConns, + MaxIdleConns uint32 +} + +func NewConnectionInfo(openConns, idleConns uint32, pusherRatio float64) (*ConnectionInfo, error) { + if pusherRatio < 0 || pusherRatio > 1 { + return nil, errors.New("EventPushConnRatio must be between 0 and 1") + } + if openConns < 2 { + return nil, errors.New("MaxOpenConns of the database must be higher that 1") + } + + info := new(ConnectionInfo) + + info.EventstorePusher.MaxOpenConns = uint32(pusherRatio * float64(openConns)) + info.EventstorePusher.MaxIdleConns = uint32(pusherRatio * float64(idleConns)) + + if info.EventstorePusher.MaxOpenConns < 1 && pusherRatio > 0 { + info.EventstorePusher.MaxOpenConns = 1 + } + if info.EventstorePusher.MaxIdleConns < 1 && pusherRatio > 0 { + info.EventstorePusher.MaxIdleConns = 1 + } + + info.ZITADEL.MaxOpenConns = openConns - info.EventstorePusher.MaxOpenConns + info.ZITADEL.MaxIdleConns = idleConns - info.EventstorePusher.MaxIdleConns + + return info, nil +} diff --git a/internal/database/mock/sql_mock.go b/internal/database/mock/sql_mock.go new file mode 100644 index 000000000..07d74151e --- /dev/null +++ b/internal/database/mock/sql_mock.go @@ -0,0 +1,129 @@ +package mock + +import ( + "database/sql" + "database/sql/driver" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +type SQLMock struct { + DB *sql.DB + mock sqlmock.Sqlmock +} + +type expectation func(m sqlmock.Sqlmock) + +func NewSQLMock(t *testing.T, expectations ...expectation) *SQLMock { + db, mock, err := sqlmock.New( + sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual), + ) + if err != nil { + t.Fatal("create mock failed", err) + } + + for _, expectation := range expectations { + expectation(mock) + } + + return &SQLMock{ + DB: db, + mock: mock, + } +} + +func (m *SQLMock) Assert(t *testing.T) { + t.Helper() + + if err := m.mock.ExpectationsWereMet(); err != nil { + t.Errorf("expectations not met: %v", err) + } + + m.DB.Close() +} + +func ExpectBegin(err error) expectation { + return func(m sqlmock.Sqlmock) { + e := m.ExpectBegin() + if err != nil { + e.WillReturnError(err) + } + } +} + +type ExecOpt func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec + +func WithExecArgs(args ...driver.Value) ExecOpt { + return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec { + return e.WithArgs(args...) + } +} + +func WithExecErr(err error) ExecOpt { + return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec { + return e.WillReturnError(err) + } +} + +func WithExecNoRowsAffected() ExecOpt { + return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec { + return e.WillReturnResult(driver.ResultNoRows) + } +} + +func WithExecRowsAffected(affected driver.RowsAffected) ExecOpt { + return func(e *sqlmock.ExpectedExec) *sqlmock.ExpectedExec { + return e.WillReturnResult(affected) + } +} + +func ExcpectExec(stmt string, opts ...ExecOpt) expectation { + return func(m sqlmock.Sqlmock) { + e := m.ExpectExec(stmt) + for _, opt := range opts { + e = opt(e) + } + } +} + +type QueryOpt func(e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery + +func WithQueryArgs(args ...driver.Value) QueryOpt { + return func(e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery { + return e.WithArgs(args...) + } +} + +func WithQueryErr(err error) QueryOpt { + return func(e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery { + return e.WillReturnError(err) + } +} + +func WithQueryResult(columns []string, rows [][]driver.Value) QueryOpt { + return func(e *sqlmock.ExpectedQuery) *sqlmock.ExpectedQuery { + mockedRows := sqlmock.NewRows(columns) + for _, row := range rows { + mockedRows = mockedRows.AddRow(row...) + } + return e.WillReturnRows(mockedRows) + } +} + +func ExpectQuery(stmt string, opts ...QueryOpt) expectation { + return func(m sqlmock.Sqlmock) { + e := m.ExpectQuery(stmt) + for _, opt := range opts { + e = opt(e) + } + } +} + +type AnyType[T interface{}] struct{} + +// Match satisfies sqlmock.Argument interface +func (a AnyType[T]) Match(v driver.Value) bool { + return reflect.TypeOf(new(T)).Elem().Kind().String() == reflect.TypeOf(v).Kind().String() +} diff --git a/internal/database/postgres/config.go b/internal/database/postgres/config.go deleted file mode 100644 index bacd09948..000000000 --- a/internal/database/postgres/config.go +++ /dev/null @@ -1,172 +0,0 @@ -package postgres - -import ( - "database/sql" - "strconv" - "strings" - "time" - - "github.com/mitchellh/mapstructure" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/database/dialect" -) - -const ( - sslDisabledMode = "disable" - sslRequireMode = "require" - sslAllowMode = "allow" - sslPreferMode = "prefer" -) - -type Config struct { - Host string - Port int32 - Database string - MaxOpenConns uint32 - MaxIdleConns uint32 - MaxConnLifetime time.Duration - MaxConnIdleTime time.Duration - User User - Admin User - - //Additional options to be appended as options= - //The value will be taken as is. Multiple options are space separated. - Options string -} - -func (c *Config) MatchName(name string) bool { - for _, key := range []string{"pg", "postgres"} { - if strings.TrimSpace(strings.ToLower(name)) == key { - return true - } - } - return false -} - -func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), - WeaklyTypedInput: true, - Result: c, - }) - if err != nil { - return nil, err - } - - for _, config := range configs { - if err = decoder.Decode(config); err != nil { - return nil, err - } - } - return c, nil -} - -func (c *Config) Connect(useAdmin bool) (*sql.DB, error) { - db, err := sql.Open("pgx", c.String(useAdmin)) - if err != nil { - return nil, err - } - - db.SetMaxOpenConns(int(c.MaxOpenConns)) - db.SetMaxIdleConns(int(c.MaxIdleConns)) - db.SetConnMaxLifetime(c.MaxConnLifetime) - db.SetConnMaxIdleTime(c.MaxConnIdleTime) - - return db, nil -} - -func (c *Config) DatabaseName() string { - return c.Database -} - -func (c *Config) Username() string { - return c.User.Username -} - -func (c *Config) Password() string { - return c.User.Password -} - -func (c *Config) Type() string { - return "postgres" -} - -func (c *Config) Timetravel(time.Duration) string { - return "" -} - -type User struct { - Username string - Password string - SSL SSL -} - -type SSL struct { - // type of connection security - Mode string - // RootCert Path to the CA certificate - RootCert string - // Cert Path to the client certificate - Cert string - // Key Path to the client private key - Key string -} - -func (s *Config) checkSSL(user User) { - if user.SSL.Mode == sslDisabledMode || user.SSL.Mode == "" { - user.SSL = SSL{Mode: sslDisabledMode} - return - } - - if user.SSL.Mode == sslRequireMode || user.SSL.Mode == sslAllowMode || user.SSL.Mode == sslPreferMode { - return - } - - if user.SSL.RootCert == "" { - logging.WithFields( - "cert set", user.SSL.Cert != "", - "key set", user.SSL.Key != "", - "rootCert set", user.SSL.RootCert != "", - ).Fatal("at least ssl root cert has to be set") - } -} - -func (c Config) String(useAdmin bool) string { - user := c.User - if useAdmin { - user = c.Admin - } - c.checkSSL(user) - fields := []string{ - "host=" + c.Host, - "port=" + strconv.Itoa(int(c.Port)), - "user=" + user.Username, - "application_name=zitadel", - "sslmode=" + user.SSL.Mode, - } - if c.Options != "" { - fields = append(fields, "options="+c.Options) - } - if user.Password != "" { - fields = append(fields, "password="+user.Password) - } - if !useAdmin { - fields = append(fields, "dbname="+c.Database) - } else { - fields = append(fields, "dbname=postgres") - } - if user.SSL.Mode != sslDisabledMode { - if user.SSL.RootCert != "" { - fields = append(fields, "sslrootcert="+user.SSL.RootCert) - } - if user.SSL.Cert != "" { - fields = append(fields, "sslcert="+user.SSL.Cert) - } - if user.SSL.Key != "" { - fields = append(fields, "sslkey="+user.SSL.Key) - } - } - - return strings.Join(fields, " ") -} diff --git a/internal/database/postgres/pg.go b/internal/database/postgres/pg.go index 562d79e27..f46067a08 100644 --- a/internal/database/postgres/pg.go +++ b/internal/database/postgres/pg.go @@ -1,9 +1,14 @@ package postgres import ( + "database/sql" + "strconv" + "strings" + "time" - //sql import _ "github.com/jackc/pgx/v4/stdlib" + "github.com/mitchellh/mapstructure" + "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/database/dialect" ) @@ -12,3 +17,176 @@ func init() { config := &Config{} dialect.Register(config, config, false) } + +const ( + sslDisabledMode = "disable" + sslRequireMode = "require" + sslAllowMode = "allow" + sslPreferMode = "prefer" +) + +type Config struct { + Host string + Port int32 + Database string + EventPushConnRatio float64 + MaxOpenConns uint32 + MaxIdleConns uint32 + MaxConnLifetime time.Duration + MaxConnIdleTime time.Duration + User User + Admin User + // Additional options to be appended as options= + // The value will be taken as is. Multiple options are space separated. + Options string +} + +func (c *Config) MatchName(name string) bool { + for _, key := range []string{"pg", "postgres"} { + if strings.TrimSpace(strings.ToLower(name)) == key { + return true + } + } + return false +} + +func (c *Config) Decode(configs []interface{}) (dialect.Connector, error) { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + WeaklyTypedInput: true, + Result: c, + }) + if err != nil { + return nil, err + } + + for _, config := range configs { + if err = decoder.Decode(config); err != nil { + return nil, err + } + } + + return c, nil +} + +func (c *Config) Connect(useAdmin, isEventPusher bool, pusherRatio float32, appName string) (*sql.DB, error) { + db, err := sql.Open("pgx", c.String(useAdmin, appName)) + if err != nil { + return nil, err + } + + connInfo, err := dialect.NewConnectionInfo(c.MaxOpenConns, c.MaxIdleConns, float64(pusherRatio)) + if err != nil { + return nil, err + } + + var maxConns, maxIdleConns uint32 + if isEventPusher { + maxConns = connInfo.EventstorePusher.MaxOpenConns + maxIdleConns = connInfo.EventstorePusher.MaxIdleConns + } else { + maxConns = connInfo.ZITADEL.MaxOpenConns + maxIdleConns = connInfo.ZITADEL.MaxIdleConns + } + db.SetMaxOpenConns(int(maxConns)) + db.SetMaxIdleConns(int(maxIdleConns)) + db.SetConnMaxLifetime(c.MaxConnLifetime) + db.SetConnMaxIdleTime(c.MaxConnIdleTime) + + return db, nil +} + +func (c *Config) DatabaseName() string { + return c.Database +} + +func (c *Config) Username() string { + return c.User.Username +} + +func (c *Config) Password() string { + return c.User.Password +} + +func (c *Config) Type() string { + return "postgres" +} + +func (c *Config) Timetravel(time.Duration) string { + return "" +} + +type User struct { + Username string + Password string + SSL SSL +} + +type SSL struct { + // type of connection security + Mode string + // RootCert Path to the CA certificate + RootCert string + // Cert Path to the client certificate + Cert string + // Key Path to the client private key + Key string +} + +func (s *Config) checkSSL(user User) { + if user.SSL.Mode == sslDisabledMode || user.SSL.Mode == "" { + user.SSL = SSL{Mode: sslDisabledMode} + return + } + + if user.SSL.Mode == sslRequireMode || user.SSL.Mode == sslAllowMode || user.SSL.Mode == sslPreferMode { + return + } + + if user.SSL.RootCert == "" { + logging.WithFields( + "cert set", user.SSL.Cert != "", + "key set", user.SSL.Key != "", + "rootCert set", user.SSL.RootCert != "", + ).Fatal("at least ssl root cert has to be set") + } +} + +func (c Config) String(useAdmin bool, appName string) string { + user := c.User + if useAdmin { + user = c.Admin + } + c.checkSSL(user) + fields := []string{ + "host=" + c.Host, + "port=" + strconv.Itoa(int(c.Port)), + "user=" + user.Username, + "application_name=" + appName, + "sslmode=" + user.SSL.Mode, + } + if c.Options != "" { + fields = append(fields, "options="+c.Options) + } + if user.Password != "" { + fields = append(fields, "password="+user.Password) + } + if !useAdmin { + fields = append(fields, "dbname="+c.Database) + } else { + fields = append(fields, "dbname=postgres") + } + if user.SSL.Mode != sslDisabledMode { + if user.SSL.RootCert != "" { + fields = append(fields, "sslrootcert="+user.SSL.RootCert) + } + if user.SSL.Cert != "" { + fields = append(fields, "sslcert="+user.SSL.Cert) + } + if user.SSL.Key != "" { + fields = append(fields, "sslkey="+user.SSL.Key) + } + } + + return strings.Join(fields, " ") +} diff --git a/internal/database/type.go b/internal/database/type.go index 4b863c101..2608dc4f4 100644 --- a/internal/database/type.go +++ b/internal/database/type.go @@ -8,22 +8,19 @@ import ( "github.com/jackc/pgtype" ) -type StringArray []string +type TextArray[t ~string] []t // Scan implements the [database/sql.Scanner] interface. -func (s *StringArray) Scan(src any) error { +func (s *TextArray[t]) Scan(src any) error { array := new(pgtype.TextArray) if err := array.Scan(src); err != nil { return err } - if err := array.AssignTo(s); err != nil { - return err - } - return nil + return array.AssignTo(s) } // Value implements the [database/sql/driver.Valuer] interface. -func (s StringArray) Value() (driver.Value, error) { +func (s TextArray[t]) Value() (driver.Value, error) { if len(s) == 0 { return nil, nil } @@ -36,37 +33,37 @@ func (s StringArray) Value() (driver.Value, error) { return array.Value() } -type enumField interface { +type arrayField interface { ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 } -type EnumArray[F enumField] []F +type Array[F arrayField] []F // Scan implements the [database/sql.Scanner] interface. -func (s *EnumArray[F]) Scan(src any) error { - array := new(pgtype.Int2Array) +func (a *Array[F]) Scan(src any) error { + array := new(pgtype.Int8Array) if err := array.Scan(src); err != nil { return err } - ints := make([]int32, 0, len(array.Elements)) - if err := array.AssignTo(&ints); err != nil { + elements := make([]int64, len(array.Elements)) + if err := array.AssignTo(&elements); err != nil { return err } - *s = make([]F, len(ints)) - for i, a := range ints { - (*s)[i] = F(a) + *a = make([]F, len(elements)) + for i, element := range elements { + (*a)[i] = F(element) } return nil } // Value implements the [database/sql/driver.Valuer] interface. -func (s EnumArray[F]) Value() (driver.Value, error) { - if len(s) == 0 { +func (a Array[F]) Value() (driver.Value, error) { + if len(a) == 0 { return nil, nil } - array := pgtype.Int2Array{} - if err := array.Set(s); err != nil { + array := pgtype.Int8Array{} + if err := array.Set(a); err != nil { return nil, err } diff --git a/internal/database/type_test.go b/internal/database/type_test.go index 4586f1413..9ac777dab 100644 --- a/internal/database/type_test.go +++ b/internal/database/type_test.go @@ -117,3 +117,85 @@ func TestMap_Value(t *testing.T) { }) } } + +func TestArray_ScanInt32(t *testing.T) { + type args struct { + src any + } + type res[V arrayField] struct { + want Array[V] + err bool + } + type testCase[V arrayField] struct { + name string + m Array[V] + args args + res[V] + } + tests := []testCase[int32]{ + { + "number", + Array[int32]{}, + args{src: "{1,2}"}, + res[int32]{ + want: []int32{1, 2}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.m.Scan(tt.args.src); (err != nil) != tt.res.err { + t.Errorf("Scan() error = %v, wantErr %v", err, tt.res.err) + } + + assert.Equal(t, tt.res.want, tt.m) + }) + } +} + +func TestArray_Value(t *testing.T) { + type res struct { + want driver.Value + err bool + } + type testCase[V arrayField] struct { + name string + a Array[V] + res res + } + tests := []testCase[int32]{ + { + "nil", + nil, + res{ + want: nil, + }, + }, + { + "empty", + Array[int32]{}, + res{ + want: nil, + }, + }, + { + "set", + Array[int32]([]int32{1, 2}), + res{ + want: driver.Value(string([]byte(`{1,2}`))), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.a.Value() + if tt.res.err { + assert.Error(t, err) + } + if !tt.res.err { + require.NoError(t, err) + assert.Equalf(t, tt.res.want, got, "Value()") + } + }) + } +} diff --git a/internal/eventstore/aggregate.go b/internal/eventstore/aggregate.go index 15ffe8622..e6b90fd88 100644 --- a/internal/eventstore/aggregate.go +++ b/internal/eventstore/aggregate.go @@ -8,7 +8,7 @@ import ( type aggregateOpt func(*Aggregate) -//NewAggregate is the default constructor of an aggregate +// NewAggregate is the default constructor of an aggregate // opts overwrite values calculated by given parameters func NewAggregate( ctx context.Context, @@ -32,7 +32,7 @@ func NewAggregate( return a } -//WithResourceOwner overwrites the resource owner of the aggregate +// WithResourceOwner overwrites the resource owner of the aggregate // by default the resource owner is set by the context func WithResourceOwner(resourceOwner string) aggregateOpt { return func(aggregate *Aggregate) { @@ -40,36 +40,48 @@ func WithResourceOwner(resourceOwner string) aggregateOpt { } } -//AggregateFromWriteModel maps the given WriteModel to an Aggregate +// WithInstanceID overwrites the instance id of the aggregate +// by default the instance is set by the context +func WithInstanceID(id string) aggregateOpt { + return func(aggregate *Aggregate) { + aggregate.InstanceID = id + } +} + +// AggregateFromWriteModel maps the given WriteModel to an Aggregate func AggregateFromWriteModel( wm *WriteModel, typ AggregateType, version Version, ) *Aggregate { - return &Aggregate{ - ID: wm.AggregateID, - Type: typ, - ResourceOwner: wm.ResourceOwner, - InstanceID: wm.InstanceID, - Version: version, - } + return NewAggregate( + context.Background(), + wm.AggregateID, + typ, + version, + WithResourceOwner(wm.ResourceOwner), + WithInstanceID(wm.InstanceID), + ) } -//Aggregate is the basic implementation of Aggregater +// Aggregate is the basic implementation of Aggregater type Aggregate struct { - //ID is the unique identitfier of this aggregate + // ID is the unique identitfier of this aggregate ID string `json:"-"` - //Type is the name of the aggregate. + // Type is the name of the aggregate. Type AggregateType `json:"-"` - //ResourceOwner is the org this aggregates belongs to + // ResourceOwner is the org this aggregates belongs to ResourceOwner string `json:"-"` - //InstanceID is the instance this aggregate belongs to + // InstanceID is the instance this aggregate belongs to InstanceID string `json:"-"` - //Version is the semver this aggregate represents + // Version is the semver this aggregate represents Version Version `json:"-"` } -func isAggreagteTypes(a Aggregate, types ...AggregateType) bool { +// AggregateType is the object name +type AggregateType string + +func isAggreagteTypes(a *Aggregate, types ...AggregateType) bool { for _, typ := range types { if a.Type == typ { return true @@ -78,7 +90,7 @@ func isAggreagteTypes(a Aggregate, types ...AggregateType) bool { return false } -func isAggregateIDs(a Aggregate, ids ...string) bool { +func isAggregateIDs(a *Aggregate, ids ...string) bool { for _, id := range ids { if a.ID == id { return true diff --git a/internal/eventstore/bench_payload.txt b/internal/eventstore/bench_payload.txt new file mode 100644 index 000000000..c8f5422c2 --- /dev/null +++ b/internal/eventstore/bench_payload.txt @@ -0,0 +1,33 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nec pharetra neque. Nam viverra elit lorem, sit amet euismod velit pulvinar vulputate. Donec bibendum convallis sodales. Pellentesque mattis massa id hendrerit suscipit. Vivamus a placerat mauris. Cras laoreet sapien eget commodo aliquet. Integer bibendum gravida augue, ultricies varius felis porttitor sed. Nullam scelerisque neque nec magna mattis rhoncus. Curabitur dictum luctus erat, vel dapibus est porta hendrerit. Donec in mauris eleifend, imperdiet nunc in, condimentum mi. Suspendisse viverra rhoncus pharetra. Sed ornare ipsum vitae eros consequat rutrum. In ullamcorper non ipsum vel aliquam. Praesent at tortor ut metus elementum mollis. Morbi orci nisi, feugiat placerat tempor id, imperdiet sed diam. + +Quisque finibus sit amet erat quis lobortis. Praesent sit amet eros lectus. Pellentesque viverra purus in augue pretium, vitae ultricies orci fermentum. Sed ut eleifend metus. Suspendisse eget facilisis velit. Vestibulum sagittis turpis felis, in ultricies mauris sagittis sed. Donec facilisis suscipit placerat. Ut consequat varius elit ac semper. Nulla convallis nisi eu lorem porttitor posuere. Aliquam molestie egestas odio a scelerisque. Nunc aliquet dui eget ipsum hendrerit, et aliquet ligula vulputate. Duis ac ullamcorper tellus. Sed sit amet semper magna, ut blandit neque. Mauris vitae sem tempor, ullamcorper velit sit amet, rutrum sapien. Quisque scelerisque sollicitudin lectus quis dictum. + +Duis convallis, dui aliquet imperdiet aliquam, nisl sem vehicula nisi, eleifend dignissim velit nunc nec sem. In euismod laoreet lacinia. Etiam euismod risus neque, sed dapibus nisl iaculis non. Vivamus porta nec risus ut tincidunt. Sed elementum metus at mauris pellentesque, ultrices pharetra arcu accumsan. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus risus mi, mollis eget condimentum sed, pulvinar viverra nunc. Nam sit amet scelerisque mauris. + +Pellentesque vitae tellus blandit ligula gravida iaculis. Pellentesque nec ex tellus. Vivamus egestas metus lacus, non iaculis est rutrum sodales. Phasellus eu mi tempus ligula mollis molestie. Suspendisse sit amet urna faucibus, tincidunt sem nec, faucibus massa. Integer vitae dui odio. Praesent finibus, quam posuere sodales maximus, libero quam vehicula massa, non dictum est risus non dui. Etiam elit risus, molestie et viverra in, dignissim et tortor. Curabitur posuere nunc id ante hendrerit, non semper neque rhoncus. + +Pellentesque in libero in dolor euismod elementum. Vestibulum malesuada sodales lacinia. Praesent tellus nisl, ultrices vel massa eu, bibendum tincidunt eros. Pellentesque in rhoncus mi. Nulla sit amet lectus eleifend, varius quam sed, hendrerit risus. Sed molestie ipsum vel sapien condimentum blandit. Etiam ex nibh, dignissim et vestibulum ac, tempus et lectus. Mauris elit libero, tempus non scelerisque at, lacinia id elit. Integer erat velit, mollis vel laoreet eu, interdum feugiat mauris. Praesent et tincidunt lorem. Praesent finibus lobortis orci, non sagittis turpis rutrum et. + +Donec et nisi nec neque condimentum sodales nec eget nisi. Nulla facilisi. Maecenas convallis, enim eu tristique rhoncus, neque urna aliquet lectus, sit amet porttitor eros neque nec ante. Suspendisse malesuada leo ut leo molestie iaculis. Nullam pretium ac eros vel finibus. Curabitur interdum iaculis enim vel imperdiet. Nam rutrum facilisis nisl, ut faucibus urna congue nec. Aenean ac diam magna. + +Curabitur tempor, magna in eleifend tincidunt, nulla quam pellentesque augue, et hendrerit dui quam quis erat. Praesent id odio sed arcu eleifend volutpat. Integer ut massa sit amet ipsum egestas facilisis. Nulla facilisis mi et velit cursus, non sagittis turpis blandit. Duis iaculis neque a sapien imperdiet pellentesque. Nam urna nisi, euismod eu velit ac, mattis efficitur lorem. Duis in dignissim ante, a elementum felis. Quisque vestibulum malesuada malesuada. Donec ultricies, purus sed tincidunt efficitur, nisl nunc aliquet orci, at euismod est magna id dolor. Aliquam ornare placerat ex, at vulputate dui sagittis nec. Proin lorem lorem, pharetra ut lectus et, gravida euismod augue. Nullam aliquam id neque a tristique. Quisque non ante a orci porta vulputate eu non eros. Vestibulum velit velit, placerat sit amet malesuada quis, auctor at nunc. + +Phasellus viverra lacus sed metus cursus fermentum. Fusce at congue nisl, at pharetra nunc. Nam maximus viverra dolor, sit amet vestibulum erat dignissim sit amet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam at pellentesque mauris. Morbi vestibulum diam et vehicula suscipit. Suspendisse ex purus, varius et efficitur id, blandit a libero. Sed dapibus, neque eget finibus vulputate, ipsum sapien aliquam risus, convallis varius nulla velit a massa. Quisque eleifend ligula in sapien bibendum maximus. In ut bibendum nisi, fringilla mollis ex. + +Suspendisse a est vel neque venenatis mollis ac egestas quam. Aenean at pretium massa, vitae rutrum est. Quisque et turpis velit. Mauris egestas nisl non mattis tempus. Pellentesque et magna nulla. Sed dolor odio, vehicula a lacus a, placerat pellentesque ante. Nam maximus dui fermentum, ornare ipsum ut, scelerisque odio. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam ac dictum mi. Integer porta dapibus orci, sollicitudin pharetra arcu mollis sed. Donec iaculis quam urna, ac tempus erat auctor eu. Maecenas pulvinar bibendum justo, sit amet blandit tortor. Maecenas id facilisis diam, a viverra odio. + +Proin at ullamcorper orci. Pellentesque dapibus, mauris ut auctor rutrum, elit arcu fermentum dolor, sit amet porta leo lacus sed sapien. Fusce pharetra nulla a venenatis tempor. Curabitur ut nibh nunc. Nunc vel libero ante. Fusce quis erat sit amet dui facilisis laoreet. Sed egestas neque quam, sed mollis sem scelerisque imperdiet. Integer vulputate orci et eros scelerisque, sed feugiat lorem tincidunt. Fusce porta semper nulla, eget sollicitudin mi tincidunt quis. Nulla facilisi. Duis suscipit turpis id sem faucibus bibendum. + +Cras elementum auctor mauris, ac finibus nisi fermentum non. Fusce congue enim mi, ac viverra lectus rutrum in. Curabitur sodales nibh est, in scelerisque dolor lobortis non. Duis non massa non ante finibus vehicula sed at est. Nulla facilisis porta magna, non convallis urna efficitur eget. Nunc nec libero nec felis facilisis sagittis. Cras aliquet neque et sapien dignissim ultrices. Donec blandit est metus. Vivamus pellentesque nec risus et tincidunt. Etiam nulla nibh, vehicula et tincidunt sit amet, porttitor a purus. Sed vel leo ante. Fusce purus felis, maximus sed erat ac, tincidunt volutpat felis. Vestibulum a ullamcorper magna. In luctus fringilla lectus, viverra vehicula velit. Suspendisse at nisl eget felis pretium faucibus tempor at nibh. + +Pellentesque vitae ligula suscipit, fermentum mauris eget, mollis magna. Duis posuere iaculis enim sit amet mattis. Donec tincidunt libero sapien, vitae convallis risus fringilla quis. Suspendisse elementum nisl urna. Aliquam erat volutpat. Mauris lacinia elit quis turpis lacinia rutrum. Donec varius a sapien et maximus. Nullam nec finibus libero. Duis pulvinar est urna, eleifend faucibus urna fringilla sed. Pellentesque pretium fermentum est, a dignissim ante cursus ut. + +Ut et massa finibus, pulvinar metus quis, pulvinar nulla. Vestibulum euismod congue euismod. Praesent tincidunt scelerisque suscipit. Sed condimentum massa quis cursus sodales. Vestibulum feugiat elit ac lobortis porta. Phasellus luctus aliquet enim ac efficitur. Nam accumsan est nunc, non malesuada nisl pretium ac. Integer id turpis hendrerit, rutrum nibh malesuada, mollis ipsum. Etiam porta aliquam leo, eget luctus nunc pretium eu. Quisque molestie risus sem, vel commodo enim ultricies tempus. Nulla cursus facilisis lacus nec aliquet. Nullam consequat arcu in mi vehicula, sit amet viverra lectus lacinia. Aliquam congue, sapien sed mattis mollis, lacus felis ultricies lectus, sed ultricies augue tellus vel purus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; + +Integer sagittis, eros luctus semper malesuada, leo est sodales lacus, quis mollis tellus ex in justo. Donec ante nibh, viverra quis nibh quis, tempor interdum sem. Donec eu consequat sapien. Pellentesque aliquam neque mauris, et dictum arcu ultricies eu. Sed mattis risus eu turpis sagittis placerat. Nunc viverra mi elit, ac dapibus leo malesuada nec. Nunc eget augue ut massa condimentum faucibus sed vitae justo. Nunc leo lectus, iaculis in lobortis ac, dictum ut metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper vestibulum lacinia. + +Quisque ornare gravida libero et laoreet. Morbi condimentum aliquam libero, sed interdum risus efficitur nec. Pellentesque in justo blandit, varius eros non, placerat nisl. Mauris turpis sem, congue at egestas sed, luctus id sapien. Maecenas volutpat, massa sit amet cursus interdum, augue odio fermentum orci, eget accumsan metus mi a augue. Quisque pharetra efficitur turpis, in mattis ipsum suscipit quis. Duis eu consectetur libero. Aenean imperdiet purus nec nibh luctus, sit amet blandit leo euismod. + +Donec placerat ut urna vitae porta. Proin ipsum elit, consectetur quis magna at, ultrices commodo lectus. Nulla cursus felis sed libero laoreet blandit. Maecenas mollis, sapien eget aliquet maximus, est tellus consequat dolor, eu tempor sapien ante sit amet arcu. Integer et lectus mi. Sed ullamcorper, enim sed porttitor commodo, sem leo rhoncus velit, sed gravida ipsum est nec ante. Aenean malesuada metus ornare mattis gravida. Fusce porttitor mattis dolor, quis convallis sem. Sed sed ex nisi. Etiam lacus felis, pulvinar et rutrum sed, lobortis nec ex. Pellentesque at dignissim metus. Nunc quis pellentesque ligula. Maecenas ut neque eget dolor ultricies condimentum. Suspendisse eget luctus dolor. + +Etiam vitae mattis arcu. Morbi faucibus et justo vel malesuada. Suspendisse semper molestie lorem in tempus. Aliquam ut mauris non velit laoreet sagittis. Sed ac efficitur nibh. Praesent commodo erat a nunc placerat vestibulum. Phasellus laoreet vitae tortor nec aliquet. Fusce nibh augue, mollis sed urna ut, tristique venenatis arcu. diff --git a/internal/eventstore/config.go b/internal/eventstore/config.go index 21011dea6..96e788f50 100644 --- a/internal/eventstore/config.go +++ b/internal/eventstore/config.go @@ -2,25 +2,11 @@ package eventstore import ( "time" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore/repository" - z_sql "github.com/zitadel/zitadel/internal/eventstore/repository/sql" ) type Config struct { - PushTimeout time.Duration - Client *database.DB - AllowOrderByCreationDate bool + PushTimeout time.Duration - repo repository.Repository -} - -func TestConfig(repo repository.Repository) *Config { - return &Config{repo: repo} -} - -func Start(config *Config) (*Eventstore, error) { - config.repo = z_sql.NewCRDB(config.Client, config.AllowOrderByCreationDate) - return NewEventstore(config), nil + Pusher Pusher + Querier Querier } diff --git a/internal/eventstore/event.go b/internal/eventstore/event.go index 51dad061e..6469db5d6 100644 --- a/internal/eventstore/event.go +++ b/internal/eventstore/event.go @@ -1,51 +1,102 @@ package eventstore import ( + "encoding/json" + "reflect" "time" + + "github.com/zitadel/zitadel/internal/errors" ) +type action interface { + Aggregate() *Aggregate + + // Creator is the userid of the user which created the action + Creator() string + // Type describes the action + Type() EventType + // Revision of the action + Revision() uint16 +} + // Command is the intend to store an event into the eventstore type Command interface { - //Aggregate is the metadata of an aggregate - Aggregate() Aggregate - // EditorService is the service who wants to push the event - EditorService() string - //EditorUser is the user who wants to push the event - EditorUser() string - //KeyType must return an event type which should be unique in the aggregate - Type() EventType - //Data returns the payload of the event. It represent the changed fields by the event + action + // Payload returns the payload of the event. It represent the changed fields by the event // valid types are: - // * nil (no payload), - // * json byte array - // * struct which can be marshalled to json - // * pointer to struct which can be marshalled to json - Data() interface{} - //UniqueConstraints should be added for unique attributes of an event, if nil constraints will not be checked - UniqueConstraints() []*EventUniqueConstraint + // * nil: no payload + // * struct: which can be marshalled to json + // * pointer: to struct which can be marshalled to json + // * []byte: json marshalled data + Payload() any + // UniqueConstraints should be added for unique attributes of an event, if nil constraints will not be checked + UniqueConstraints() []*UniqueConstraint } // Event is a stored activity type Event interface { - // EditorService is the service who pushed the event - EditorService() string - //EditorUser is the user who pushed the event - EditorUser() string - //KeyType is the type of the event - Type() EventType - - Aggregate() Aggregate + action + // Sequence of the event in the aggregate Sequence() uint64 - CreationDate() time.Time - //PreviousAggregateSequence returns the previous sequence of the aggregate root (e.g. for org.42508134) - PreviousAggregateSequence() uint64 - //PreviousAggregateTypeSequence returns the previous sequence of the aggregate type (e.g. for org) - PreviousAggregateTypeSequence() uint64 - //DataAsBytes returns the payload of the event. It represent the changed fields by the event + // CreatedAt is the time the event was created at + CreatedAt() time.Time + // Position is the global position of the event + Position() float64 + + // Unmarshal parses the payload and stores the result + // in the value pointed to by ptr. If ptr is nil or not a pointer, + // Unmarshal returns an error + Unmarshal(ptr any) error + + // Deprecated: only use for migration DataAsBytes() []byte } +type EventType string + +func EventData(event Command) ([]byte, error) { + switch data := event.Payload().(type) { + case nil: + return nil, nil + case []byte: + if json.Valid(data) { + return data, nil + } + return nil, errors.ThrowInvalidArgument(nil, "V2-6SbbS", "data bytes are not json") + } + dataType := reflect.TypeOf(event.Payload()) + if dataType.Kind() == reflect.Ptr { + dataType = dataType.Elem() + } + if dataType.Kind() == reflect.Struct { + dataBytes, err := json.Marshal(event.Payload()) + if err != nil { + return nil, errors.ThrowInvalidArgument(err, "V2-xG87M", "could not marshal data") + } + return dataBytes, nil + } + return nil, errors.ThrowInvalidArgument(nil, "V2-91NRm", "wrong type of event data") +} + +type BaseEventSetter[T any] interface { + Event + SetBaseEvent(*BaseEvent) + *T +} + +func GenericEventMapper[T any, PT BaseEventSetter[T]](event Event) (Event, error) { + e := PT(new(T)) + e.SetBaseEvent(BaseEventFromRepo(event)) + + err := event.Unmarshal(e) + if err != nil { + return nil, errors.ThrowInternal(err, "ES-Thai6", "unable to unmarshal event") + } + + return e, nil +} + func isEventTypes(event Event, types ...EventType) bool { for _, typ := range types { if event.Type() == typ { diff --git a/internal/eventstore/event_base.go b/internal/eventstore/event_base.go index cb404feba..5bb117b14 100644 --- a/internal/eventstore/event_base.go +++ b/internal/eventstore/event_base.go @@ -2,21 +2,27 @@ package eventstore import ( "context" + "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/service" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) -//BaseEvent represents the minimum metadata of an event +var ( + _ Event = (*BaseEvent)(nil) +) + +// BaseEvent represents the minimum metadata of an event type BaseEvent struct { + ID string EventType EventType `json:"-"` - aggregate Aggregate + Agg *Aggregate - sequence uint64 - creationDate time.Time + Seq uint64 + Pos float64 + Creation time.Time previousAggregateSequence uint64 previousAggregateTypeSequence uint64 @@ -27,78 +33,91 @@ type BaseEvent struct { Data []byte `json:"-"` } +// Position implements Event. +func (e *BaseEvent) Position() float64 { + return e.Pos +} + // EditorService implements Command func (e *BaseEvent) EditorService() string { return e.Service } -//EditorUser implements Command +// EditorUser implements Command func (e *BaseEvent) EditorUser() string { return e.User } -//Type implements Command +// Creator implements action +func (e *BaseEvent) Creator() string { + return e.EditorUser() +} + +// Type implements action func (e *BaseEvent) Type() EventType { return e.EventType } -//Sequence is an upcounting unique number of the event +// Sequence is an upcounting unique number of the event func (e *BaseEvent) Sequence() uint64 { - return e.sequence + return e.Seq } -//CreationDate is the the time, the event is inserted into the eventstore +// CreationDate is the the time, the event is inserted into the eventstore func (e *BaseEvent) CreationDate() time.Time { - return e.creationDate + return e.Creation } -//Aggregate represents the metadata of the event's aggregate -func (e *BaseEvent) Aggregate() Aggregate { - return e.aggregate +// CreatedAt implements Event +func (e *BaseEvent) CreatedAt() time.Time { + return e.CreationDate() } -//Data returns the payload of the event. It represent the changed fields by the event +// Aggregate implements action +func (e *BaseEvent) Aggregate() *Aggregate { + return e.Agg +} + +// Data returns the payload of the event. It represent the changed fields by the event func (e *BaseEvent) DataAsBytes() []byte { return e.Data } -//PreviousAggregateSequence implements EventReader -func (e *BaseEvent) PreviousAggregateSequence() uint64 { - return e.previousAggregateSequence +// Revision implements action +func (*BaseEvent) Revision() uint16 { + return 0 } -//PreviousAggregateTypeSequence implements EventReader -func (e *BaseEvent) PreviousAggregateTypeSequence() uint64 { - return e.previousAggregateTypeSequence +// Unmarshal implements Event +func (e *BaseEvent) Unmarshal(ptr any) error { + if len(e.Data) == 0 { + return nil + } + return json.Unmarshal(e.Data, ptr) } -//BaseEventFromRepo maps a stored event to a BaseEvent -func BaseEventFromRepo(event *repository.Event) *BaseEvent { +const defaultService = "zitadel" + +// BaseEventFromRepo maps a stored event to a BaseEvent +func BaseEventFromRepo(event Event) *BaseEvent { return &BaseEvent{ - aggregate: Aggregate{ - ID: event.AggregateID, - Type: AggregateType(event.AggregateType), - ResourceOwner: event.ResourceOwner.String, - InstanceID: event.InstanceID, - Version: Version(event.Version), - }, - EventType: EventType(event.Type), - creationDate: event.CreationDate, - sequence: event.Sequence, - previousAggregateSequence: event.PreviousAggregateSequence, - previousAggregateTypeSequence: event.PreviousAggregateTypeSequence, - Service: event.EditorService, - User: event.EditorUser, - Data: event.Data, + Agg: event.Aggregate(), + EventType: event.Type(), + Creation: event.CreatedAt(), + Seq: event.Sequence(), + Service: defaultService, + User: event.Creator(), + Data: event.DataAsBytes(), + Pos: event.Position(), } } -//NewBaseEventForPush is the constructor for event's which will be pushed into the eventstore +// NewBaseEventForPush is the constructor for event's which will be pushed into the eventstore // the resource owner of the aggregate is only used if it's the first event of this aggregate type // afterwards the resource owner of the first previous events is taken func NewBaseEventForPush(ctx context.Context, aggregate *Aggregate, typ EventType) *BaseEvent { return &BaseEvent{ - aggregate: *aggregate, + Agg: aggregate, User: authz.GetCtxData(ctx).UserID, Service: service.FromContext(ctx), EventType: typ, diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index f602a53f1..2a8d8a02d 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -2,91 +2,75 @@ package eventstore import ( "context" - "database/sql" - "encoding/json" - "reflect" "sort" "sync" "time" "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) // Eventstore abstracts all functions needed to store valid events // and filters the stored events type Eventstore struct { - repo repository.Repository interceptorMutex sync.Mutex eventInterceptors map[EventType]eventTypeInterceptors eventTypes []string aggregateTypes []string PushTimeout time.Duration - instancesMu sync.Mutex - instances []string - lastInstancesQuery time.Time + pusher Pusher + querier Querier + + instances []string + lastInstanceQuery time.Time + instancesMu sync.Mutex } type eventTypeInterceptors struct { - eventMapper func(*repository.Event) (Event, error) + eventMapper func(Event) (Event, error) } func NewEventstore(config *Config) *Eventstore { return &Eventstore{ - repo: config.repo, eventInterceptors: map[EventType]eventTypeInterceptors{}, interceptorMutex: sync.Mutex{}, PushTimeout: config.PushTimeout, + + pusher: config.Pusher, + querier: config.Querier, + + instancesMu: sync.Mutex{}, } } // Health checks if the eventstore can properly work // It checks if the repository can serve load func (es *Eventstore) Health(ctx context.Context) error { - return es.repo.Health(ctx) + if err := es.pusher.Health(ctx); err != nil { + return err + } + return es.querier.Health(ctx) } // Push pushes the events in a single transaction // an event needs at least an aggregate func (es *Eventstore) Push(ctx context.Context, cmds ...Command) ([]Event, error) { - events, constraints, err := commandsToRepository(authz.GetInstance(ctx).InstanceID(), cmds) - if err != nil { - return nil, err - } - if es.PushTimeout > 0 { var cancel func() ctx, cancel = context.WithTimeout(ctx, es.PushTimeout) defer cancel() } - - err = es.repo.Push(ctx, events, constraints...) + events, err := es.pusher.Push(ctx, cmds...) if err != nil { return nil, err } - eventReaders, err := es.mapEvents(events) + mappedEvents, err := es.mapEvents(events) if err != nil { - return nil, err + return mappedEvents, err } - - go notify(eventReaders) - return eventReaders, nil -} - -func (es *Eventstore) NewInstance(ctx context.Context, instanceID string) error { - err := es.repo.CreateInstance(ctx, instanceID) - if err != nil { - return err - } - - es.instancesMu.Lock() - es.instances = append(es.instances, instanceID) - es.instancesMu.Unlock() - - return nil + es.notify(mappedEvents) + return mappedEvents, nil } func (es *Eventstore) EventTypes() []string { @@ -97,70 +81,15 @@ func (es *Eventstore) AggregateTypes() []string { return es.aggregateTypes } -func commandsToRepository(instanceID string, cmds []Command) (events []*repository.Event, constraints []*repository.UniqueConstraint, err error) { - events = make([]*repository.Event, len(cmds)) - for i, cmd := range cmds { - data, err := EventData(cmd) - if err != nil { - return nil, nil, err - } - if cmd.Aggregate().ID == "" { - return nil, nil, errors.ThrowInvalidArgument(nil, "V2-Afdfe", "aggregate id must not be empty") - } - if cmd.Aggregate().Type == "" { - return nil, nil, errors.ThrowInvalidArgument(nil, "V2-Dfg32", "aggregate type must not be empty") - } - if cmd.Type() == "" { - return nil, nil, errors.ThrowInvalidArgument(nil, "V2-Drg34", "event type must not be empty") - } - if cmd.Aggregate().Version == "" { - return nil, nil, errors.ThrowInvalidArgument(nil, "V2-Dgfg4", "aggregate version must not be empty") - } - events[i] = &repository.Event{ - AggregateID: cmd.Aggregate().ID, - AggregateType: repository.AggregateType(cmd.Aggregate().Type), - ResourceOwner: sql.NullString{String: cmd.Aggregate().ResourceOwner, Valid: cmd.Aggregate().ResourceOwner != ""}, - InstanceID: instanceID, - EditorService: cmd.EditorService(), - EditorUser: cmd.EditorUser(), - Type: repository.EventType(cmd.Type()), - Version: repository.Version(cmd.Aggregate().Version), - Data: data, - } - if len(cmd.UniqueConstraints()) > 0 { - constraints = append(constraints, uniqueConstraintsToRepository(instanceID, cmd.UniqueConstraints())...) - } - } - - return events, constraints, nil -} - -func uniqueConstraintsToRepository(instanceID string, constraints []*EventUniqueConstraint) (uniqueConstraints []*repository.UniqueConstraint) { - uniqueConstraints = make([]*repository.UniqueConstraint, len(constraints)) - for i, constraint := range constraints { - var id string - if !constraint.IsGlobal { - id = instanceID - } - uniqueConstraints[i] = &repository.UniqueConstraint{ - UniqueType: constraint.UniqueType, - UniqueField: constraint.UniqueField, - InstanceID: id, - Action: uniqueConstraintActionToRepository(constraint.Action), - ErrorMessage: constraint.ErrorMessage, - } - } - return uniqueConstraints -} - // Filter filters the stored events based on the searchQuery // and maps the events to the defined event structs func (es *Eventstore) Filter(ctx context.Context, queryFactory *SearchQueryBuilder) ([]Event, error) { - query, err := queryFactory.build(authz.GetInstance(ctx).InstanceID()) - if err != nil { - return nil, err + // make sure that the instance id is always set + if queryFactory.instanceID == nil && authz.GetInstance(ctx).InstanceID() != "" { + queryFactory.InstanceID(authz.GetInstance(ctx).InstanceID()) } - events, err := es.repo.Filter(ctx, query) + + events, err := es.querier.Filter(ctx, queryFactory) if err != nil { return nil, err } @@ -168,21 +97,14 @@ func (es *Eventstore) Filter(ctx context.Context, queryFactory *SearchQueryBuild return es.mapEvents(events) } -func (es *Eventstore) mapEvents(events []*repository.Event) (mappedEvents []Event, err error) { +func (es *Eventstore) mapEvents(events []Event) (mappedEvents []Event, err error) { mappedEvents = make([]Event, len(events)) es.interceptorMutex.Lock() defer es.interceptorMutex.Unlock() for i, event := range events { - interceptors, ok := es.eventInterceptors[EventType(event.Type)] - if !ok || interceptors.eventMapper == nil { - mappedEvents[i] = BaseEventFromRepo(event) - //TODO: return error if unable to map event - continue - // return nil, errors.ThrowPreconditionFailed(nil, "V2-usujB", "event mapper not defined") - } - mappedEvents[i], err = interceptors.eventMapper(event) + mappedEvents[i], err = es.mapEvent(event) if err != nil { return nil, err } @@ -191,6 +113,14 @@ func (es *Eventstore) mapEvents(events []*repository.Event) (mappedEvents []Even return mappedEvents, nil } +func (es *Eventstore) mapEvent(event Event) (Event, error) { + interceptors, ok := es.eventInterceptors[event.Type()] + if !ok || interceptors.eventMapper == nil { + return BaseEventFromRepo(event), nil + } + return interceptors.eventMapper(event) +} + type reducer interface { //Reduce handles the events of the internal events list // it only appends the newly added events @@ -212,12 +142,10 @@ func (es *Eventstore) FilterToReducer(ctx context.Context, searchQuery *SearchQu } // LatestSequence filters the latest sequence for the given search query -func (es *Eventstore) LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (uint64, error) { - query, err := queryFactory.build(authz.GetInstance(ctx).InstanceID()) - if err != nil { - return 0, err - } - return es.repo.LatestSequence(ctx, query) +func (es *Eventstore) LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error) { + queryFactory.InstanceID(authz.GetInstance(ctx).InstanceID()) + + return es.querier.LatestSequence(ctx, queryFactory) } // InstanceIDs returns the instance ids found by the search query @@ -226,21 +154,18 @@ func (es *Eventstore) InstanceIDs(ctx context.Context, maxAge time.Duration, for es.instancesMu.Lock() defer es.instancesMu.Unlock() - if !forceDBCall && time.Since(es.lastInstancesQuery) <= maxAge { + if !forceDBCall && time.Since(es.lastInstanceQuery) <= maxAge { return es.instances, nil } - query, err := queryFactory.build(authz.GetInstance(ctx).InstanceID()) - if err != nil { - return nil, err - } - instances, err := es.repo.InstanceIDs(ctx, query) + instances, err := es.querier.InstanceIDs(ctx, queryFactory) if err != nil { return nil, err } + if !forceDBCall { es.instances = instances - es.lastInstancesQuery = time.Now() + es.lastInstanceQuery = time.Now() } return instances, nil @@ -265,7 +190,7 @@ func (es *Eventstore) FilterToQueryReducer(ctx context.Context, r QueryReducer) } // RegisterFilterEventMapper registers a function for mapping an eventstore event to an event -func (es *Eventstore) RegisterFilterEventMapper(aggregateType AggregateType, eventType EventType, mapper func(*repository.Event) (Event, error)) *Eventstore { +func (es *Eventstore) RegisterFilterEventMapper(aggregateType AggregateType, eventType EventType, mapper func(Event) (Event, error)) *Eventstore { if mapper == nil || eventType == "" { return es } @@ -282,6 +207,24 @@ func (es *Eventstore) RegisterFilterEventMapper(aggregateType AggregateType, eve return es } +type Querier interface { + // Health checks if the connection to the storage is available + Health(ctx context.Context) error + // Filter returns all events matching the given search query + Filter(ctx context.Context, searchQuery *SearchQueryBuilder) (events []Event, err error) + // LatestSequence returns the latest sequence found by the search query + LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error) + // InstanceIDs returns the instance ids found by the search query + InstanceIDs(ctx context.Context, queryFactory *SearchQueryBuilder) ([]string, error) +} + +type Pusher interface { + // Health checks if the connection to the storage is available + Health(ctx context.Context) error + // Push stores the actions + Push(ctx context.Context, commands ...Command) (_ []Event, err error) +} + func (es *Eventstore) appendEventType(typ EventType) { i := sort.SearchStrings(es.eventTypes, string(typ)) if i < len(es.eventTypes) && es.eventTypes[i] == string(typ) { @@ -297,61 +240,3 @@ func (es *Eventstore) appendAggregateType(typ AggregateType) { } es.aggregateTypes = append(es.aggregateTypes[:i], append([]string{string(typ)}, es.aggregateTypes[i:]...)...) } - -func EventData(event Command) ([]byte, error) { - switch data := event.Data().(type) { - case nil: - return nil, nil - case []byte: - if json.Valid(data) { - return data, nil - } - return nil, errors.ThrowInvalidArgument(nil, "V2-6SbbS", "data bytes are not json") - } - dataType := reflect.TypeOf(event.Data()) - if dataType.Kind() == reflect.Ptr { - dataType = dataType.Elem() - } - if dataType.Kind() == reflect.Struct { - dataBytes, err := json.Marshal(event.Data()) - if err != nil { - return nil, errors.ThrowInvalidArgument(err, "V2-xG87M", "could not marshal data") - } - return dataBytes, nil - } - return nil, errors.ThrowInvalidArgument(nil, "V2-91NRm", "wrong type of event data") -} - -func uniqueConstraintActionToRepository(action UniqueConstraintAction) repository.UniqueConstraintAction { - switch action { - case UniqueConstraintAdd: - return repository.UniqueConstraintAdd - case UniqueConstraintRemove: - return repository.UniqueConstraintRemoved - case UniqueConstraintInstanceRemove: - return repository.UniqueConstraintInstanceRemoved - default: - return repository.UniqueConstraintAdd - } -} - -type BaseEventSetter[T any] interface { - Event - SetBaseEvent(*BaseEvent) - *T -} - -func GenericEventMapper[T any, PT BaseEventSetter[T]](event *repository.Event) (Event, error) { - e := PT(new(T)) - e.SetBaseEvent(BaseEventFromRepo(event)) - - if len(event.Data) == 0 { - return e, nil - } - err := json.Unmarshal(event.Data, e) - if err != nil { - return nil, errors.ThrowInternal(err, "V2-Thai6", "unable to unmarshal event") - } - - return e, nil -} diff --git a/internal/eventstore/eventstore_bench_test.go b/internal/eventstore/eventstore_bench_test.go new file mode 100644 index 000000000..69b958abd --- /dev/null +++ b/internal/eventstore/eventstore_bench_test.go @@ -0,0 +1,162 @@ +package eventstore_test + +import ( + "context" + _ "embed" + "fmt" + "strconv" + "testing" + + "github.com/zitadel/zitadel/internal/eventstore" +) + +//go:embed bench_payload.txt +var text string + +func Benchmark_Push_SameAggregate(b *testing.B) { + ctx := context.Background() + + smallPayload := struct { + Username string + Firstname string + Lastname string + }{ + Username: "username", + Firstname: "firstname", + Lastname: "lastname", + } + + bigPayload := struct { + Username string + Firstname string + Lastname string + Text string + }{ + Username: "username", + Firstname: "firstname", + Lastname: "lastname", + Text: text, + } + + commands := map[string][]eventstore.Command{ + "no payload one command": { + generateCommand(eventstore.AggregateType(b.Name()), "id"), + }, + "small payload one command": { + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(smallPayload)), + }, + "big payload one command": { + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(bigPayload)), + }, + "no payload multiple commands": { + generateCommand(eventstore.AggregateType(b.Name()), "id"), + generateCommand(eventstore.AggregateType(b.Name()), "id"), + generateCommand(eventstore.AggregateType(b.Name()), "id"), + }, + "mixed payload multiple command": { + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(smallPayload)), + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(bigPayload)), + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(smallPayload)), + generateCommand(eventstore.AggregateType(b.Name()), "id", withTestData(bigPayload)), + }, + } + + for cmdsKey, cmds := range commands { + for pusherKey, store := range pushers { + b.Run(fmt.Sprintf("Benchmark_Push_SameAggregate-%s-%s", pusherKey, cmdsKey), func(b *testing.B) { + b.StopTimer() + cleanupEventstore(clients[pusherKey]) + b.StartTimer() + + for n := 0; n < b.N; n++ { + _, err := store.Push(ctx, cmds...) + if err != nil { + b.Error(err) + } + } + }) + } + } +} + +func Benchmark_Push_MultipleAggregate_Parallel(b *testing.B) { + smallPayload := struct { + Username string + Firstname string + Lastname string + }{ + Username: "username", + Firstname: "firstname", + Lastname: "lastname", + } + + bigPayload := struct { + Username string + Firstname string + Lastname string + Text string + }{ + Username: "username", + Firstname: "firstname", + Lastname: "lastname", + Text: text, + } + + commandCreators := map[string]func(id string) []eventstore.Command{ + "no payload one command": func(id string) []eventstore.Command { + return []eventstore.Command{ + generateCommand(eventstore.AggregateType(b.Name()), id), + } + }, + "small payload one command": func(id string) []eventstore.Command { + return []eventstore.Command{ + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(smallPayload)), + } + }, + "big payload one command": func(id string) []eventstore.Command { + return []eventstore.Command{ + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(bigPayload)), + } + }, + "no payload multiple commands": func(id string) []eventstore.Command { + return []eventstore.Command{ + generateCommand(eventstore.AggregateType(b.Name()), id), + generateCommand(eventstore.AggregateType(b.Name()), id), + generateCommand(eventstore.AggregateType(b.Name()), id), + } + }, + "mixed payload multiple command": func(id string) []eventstore.Command { + return []eventstore.Command{ + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(smallPayload)), + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(bigPayload)), + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(smallPayload)), + generateCommand(eventstore.AggregateType(b.Name()), id, withTestData(bigPayload)), + } + }, + } + + for cmdsKey, commandCreator := range commandCreators { + for pusherKey, store := range pushers { + b.Run(fmt.Sprintf("Benchmark_Push_DifferentAggregate-%s-%s", cmdsKey, pusherKey), func(b *testing.B) { + b.StopTimer() + cleanupEventstore(clients[pusherKey]) + + ctx, cancel := context.WithCancel(context.Background()) + b.StartTimer() + + i := 0 + + b.RunParallel(func(p *testing.PB) { + for p.Next() { + i++ + _, err := store.Push(ctx, commandCreator(strconv.Itoa(i))...) + if err != nil { + b.Error(err) + } + } + }) + cancel() + }) + } + } +} diff --git a/internal/eventstore/eventstore_pusher_test.go b/internal/eventstore/eventstore_pusher_test.go new file mode 100644 index 000000000..cbd1a72e3 --- /dev/null +++ b/internal/eventstore/eventstore_pusher_test.go @@ -0,0 +1,712 @@ +package eventstore_test + +import ( + "context" + "database/sql" + "sync" + "testing" + "time" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +func TestCRDB_Push_OneAggregate(t *testing.T) { + type args struct { + ctx context.Context + commands []eventstore.Command + uniqueDataType string + uniqueDataField string + uniqueDataInstanceID string + } + type eventsRes struct { + pushedEventsCount int + uniqueCount int + assetCount int + aggType eventstore.AggregateType + aggIDs database.TextArray[string] + } + type res struct { + wantErr bool + eventsRes eventsRes + } + tests := []struct { + name string + args args + res res + }{ + { + name: "push 1 event", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "1"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + aggIDs: []string{"1"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + { + name: "push two events on agg", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "6"), + generateCommand(eventstore.AggregateType(t.Name()), "6"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 2, + aggIDs: []string{"6"}, + aggType: eventstore.AggregateType(t.Name()), + }, + }, + }, + { + name: "failed push because context canceled", + args: args{ + ctx: canceledCtx(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "9"), + }, + }, + res: res{ + wantErr: true, + eventsRes: eventsRes{ + pushedEventsCount: 0, + aggIDs: []string{"9"}, + aggType: eventstore.AggregateType(t.Name()), + }, + }, + }, + { + name: "push 1 event and add unique constraint", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "10", + generateAddUniqueConstraint("usernames", "field"), + ), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + uniqueCount: 1, + aggIDs: []string{"10"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + { + name: "push 1 event and remove unique constraint", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "11", + generateRemoveUniqueConstraint("usernames", "testremove"), + ), + }, + uniqueDataType: "usernames", + uniqueDataField: "testremove", + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + uniqueCount: 0, + aggIDs: []string{"11"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + { + name: "push 1 event and remove instance unique constraints", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "12", + generateRemoveUniqueConstraint("instance", "instanceID"), + ), + }, + uniqueDataType: "usernames", + uniqueDataField: "testremove", + uniqueDataInstanceID: "instanceID", + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + uniqueCount: 0, + aggIDs: []string{"12"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + { + name: "push 1 event and add asset", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "13"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + assetCount: 1, + aggIDs: []string{"13"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + { + name: "push 1 event and remove asset", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "14"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 1, + assetCount: 0, + aggIDs: []string{"14"}, + aggType: eventstore.AggregateType(t.Name()), + }}, + }, + } + for _, tt := range tests { + for pusherName, pusher := range pushers { + t.Run(pusherName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[pusherName])) + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: queriers["v2(inmemory)"], + Pusher: pusher, + }, + ) + + if tt.args.uniqueDataType != "" && tt.args.uniqueDataField != "" { + err := fillUniqueData(tt.args.uniqueDataType, tt.args.uniqueDataField, tt.args.uniqueDataInstanceID) + if err != nil { + t.Error("unable to prefill insert unique data: ", err) + return + } + } + if _, err := db.Push(tt.args.ctx, tt.args.commands...); (err != nil) != tt.res.wantErr { + t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) + } + + assertEventCount(t, + clients[pusherName], + database.TextArray[eventstore.AggregateType]{tt.res.eventsRes.aggType}, + tt.res.eventsRes.aggIDs, + tt.res.eventsRes.pushedEventsCount, + ) + + assertUniqueConstraint(t, clients[pusherName], tt.args.commands, tt.res.eventsRes.uniqueCount) + }) + } + } +} + +func TestCRDB_Push_MultipleAggregate(t *testing.T) { + type args struct { + commands []eventstore.Command + } + type eventsRes struct { + pushedEventsCount int + aggType database.TextArray[eventstore.AggregateType] + aggID database.TextArray[string] + } + type res struct { + wantErr bool + eventsRes eventsRes + } + tests := []struct { + name string + args args + res res + }{ + { + name: "push two aggregates", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "100"), + generateCommand(eventstore.AggregateType(t.Name()), "101"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 2, + aggID: []string{"100", "101"}, + aggType: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + { + name: "push two aggregates both multiple events", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "102"), + generateCommand(eventstore.AggregateType(t.Name()), "102"), + generateCommand(eventstore.AggregateType(t.Name()), "103"), + generateCommand(eventstore.AggregateType(t.Name()), "103"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 4, + aggID: []string{"102", "103"}, + aggType: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + { + name: "push two aggregates mixed multiple events", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "106"), + generateCommand(eventstore.AggregateType(t.Name()), "106"), + generateCommand(eventstore.AggregateType(t.Name()), "106"), + generateCommand(eventstore.AggregateType(t.Name()), "106"), + generateCommand(eventstore.AggregateType(t.Name()), "107"), + generateCommand(eventstore.AggregateType(t.Name()), "107"), + generateCommand(eventstore.AggregateType(t.Name()), "107"), + generateCommand(eventstore.AggregateType(t.Name()), "107"), + generateCommand(eventstore.AggregateType(t.Name()), "108"), + generateCommand(eventstore.AggregateType(t.Name()), "108"), + generateCommand(eventstore.AggregateType(t.Name()), "108"), + generateCommand(eventstore.AggregateType(t.Name()), "108"), + }, + }, + res: res{ + wantErr: false, + eventsRes: eventsRes{ + pushedEventsCount: 12, + aggID: []string{"106", "107", "108"}, + aggType: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + } + for _, tt := range tests { + for pusherName, pusher := range pushers { + t.Run(pusherName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[pusherName])) + + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: queriers["v2(inmemory)"], + Pusher: pusher, + }, + ) + if _, err := db.Push(context.Background(), tt.args.commands...); (err != nil) != tt.res.wantErr { + t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) + } + + assertEventCount(t, clients[pusherName], tt.res.eventsRes.aggType, tt.res.eventsRes.aggID, tt.res.eventsRes.pushedEventsCount) + }) + } + } +} + +func TestCRDB_Push_Parallel(t *testing.T) { + type args struct { + commands [][]eventstore.Command + } + type eventsRes struct { + pushedEventsCount int + aggTypes database.TextArray[eventstore.AggregateType] + aggIDs database.TextArray[string] + } + type res struct { + minErrCount int + eventsRes eventsRes + } + tests := []struct { + name string + args args + res res + }{ + { + name: "clients push different aggregates", + args: args{ + commands: [][]eventstore.Command{ + { + generateCommand(eventstore.AggregateType(t.Name()), "200"), + generateCommand(eventstore.AggregateType(t.Name()), "200"), + generateCommand(eventstore.AggregateType(t.Name()), "200"), + generateCommand(eventstore.AggregateType(t.Name()), "201"), + generateCommand(eventstore.AggregateType(t.Name()), "201"), + generateCommand(eventstore.AggregateType(t.Name()), "201"), + }, + { + generateCommand(eventstore.AggregateType(t.Name()), "202"), + generateCommand(eventstore.AggregateType(t.Name()), "203"), + generateCommand(eventstore.AggregateType(t.Name()), "203"), + }, + }, + }, + res: res{ + minErrCount: 0, + eventsRes: eventsRes{ + aggIDs: []string{"200", "201", "202", "203"}, + pushedEventsCount: 9, + aggTypes: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + { + name: "clients push same aggregates", + args: args{ + commands: [][]eventstore.Command{ + { + generateCommand(eventstore.AggregateType(t.Name()), "204"), + generateCommand(eventstore.AggregateType(t.Name()), "204"), + }, + { + generateCommand(eventstore.AggregateType(t.Name()), "204"), + generateCommand(eventstore.AggregateType(t.Name()), "204"), + }, + { + generateCommand(eventstore.AggregateType(t.Name()), "204"), + generateCommand(eventstore.AggregateType(t.Name()), "204"), + }, + { + generateCommand(eventstore.AggregateType(t.Name()), "204"), + generateCommand(eventstore.AggregateType(t.Name()), "204"), + }, + }, + }, + res: res{ + minErrCount: 1, + eventsRes: eventsRes{ + aggIDs: []string{"204"}, + pushedEventsCount: 6, + aggTypes: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + { + name: "clients push different aggregates", + args: args{ + commands: [][]eventstore.Command{ + { + generateCommand(eventstore.AggregateType(t.Name()), "207"), + generateCommand(eventstore.AggregateType(t.Name()), "207"), + generateCommand(eventstore.AggregateType(t.Name()), "207"), + generateCommand(eventstore.AggregateType(t.Name()), "207"), + generateCommand(eventstore.AggregateType(t.Name()), "207"), + generateCommand(eventstore.AggregateType(t.Name()), "207"), + }, + { + generateCommand(eventstore.AggregateType(t.Name()), "208"), + generateCommand(eventstore.AggregateType(t.Name()), "208"), + generateCommand(eventstore.AggregateType(t.Name()), "208"), + generateCommand(eventstore.AggregateType(t.Name()), "208"), + generateCommand(eventstore.AggregateType(t.Name()), "208"), + }, + }, + }, + res: res{ + minErrCount: 0, + eventsRes: eventsRes{ + aggIDs: []string{"207", "208"}, + pushedEventsCount: 11, + aggTypes: database.TextArray[eventstore.AggregateType]{eventstore.AggregateType(t.Name())}, + }, + }, + }, + } + for _, tt := range tests { + for pusherName, pusher := range pushers { + t.Run(pusherName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[pusherName])) + + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: queriers["v2(inmemory)"], + Pusher: pusher, + }, + ) + + errs := pushAggregates(db, tt.args.commands) + + if len(errs) < tt.res.minErrCount { + t.Errorf("eventstore.Push() error count = %d, wanted err count %d, errs: %v", len(errs), tt.res.minErrCount, errs) + } + + assertEventCount(t, clients[pusherName], tt.res.eventsRes.aggTypes, tt.res.eventsRes.aggIDs, tt.res.eventsRes.pushedEventsCount) + }) + } + } +} + +func TestCRDB_Push_ResourceOwner(t *testing.T) { + type args struct { + commands []eventstore.Command + } + type res struct { + resourceOwners database.TextArray[string] + } + type fields struct { + aggregateIDs database.TextArray[string] + aggregateType string + } + tests := []struct { + name string + args args + res res + fields fields + }{ + { + name: "two events of same aggregate same resource owner", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "500", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "500", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"500"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "caos"}, + }, + }, + { + name: "two events of different aggregate same resource owner", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "501", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "502", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"501", "502"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "caos"}, + }, + }, + { + name: "two events of different aggregate different resource owner", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "503", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "504", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "zitadel" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"503", "504"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "zitadel"}, + }, + }, + { + name: "events of different aggregate different resource owner", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "505", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "505", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "506", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "zitadel" }), + generateCommand(eventstore.AggregateType(t.Name()), "506", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "zitadel" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"505", "506"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "caos", "zitadel", "zitadel"}, + }, + }, + { + name: "events of different aggregate different resource owner per event", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "507", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "507", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "ignored" }), + generateCommand(eventstore.AggregateType(t.Name()), "508", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "zitadel" }), + generateCommand(eventstore.AggregateType(t.Name()), "508", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "ignored" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"507", "508"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "caos", "zitadel", "zitadel"}, + }, + }, + { + name: "events of one aggregate different resource owner per event", + args: args{ + commands: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "509", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "caos" }), + generateCommand(eventstore.AggregateType(t.Name()), "509", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "ignored" }), + generateCommand(eventstore.AggregateType(t.Name()), "509", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "ignored" }), + generateCommand(eventstore.AggregateType(t.Name()), "509", func(e *testEvent) { e.BaseEvent.Agg.ResourceOwner = "ignored" }), + }, + }, + fields: fields{ + aggregateIDs: []string{"509"}, + aggregateType: t.Name(), + }, + res: res{ + resourceOwners: []string{"caos", "caos", "caos", "caos"}, + }, + }, + } + for _, tt := range tests { + for pusherName, pusher := range pushers { + t.Run(pusherName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[pusherName])) + + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: queriers["v2(inmemory)"], + Pusher: pusher, + }, + ) + + events, err := db.Push(context.Background(), tt.args.commands...) + if err != nil { + t.Errorf("CRDB.Push() error = %v", err) + } + + if len(events) != len(tt.res.resourceOwners) { + t.Errorf("length of events (%d) and resource owners (%d) must be equal", len(events), len(tt.res.resourceOwners)) + return + } + + for i, event := range events { + if event.Aggregate().ResourceOwner != tt.res.resourceOwners[i] { + t.Errorf("resource owner not expected want: %q got: %q", tt.res.resourceOwners[i], event.Aggregate().ResourceOwner) + } + } + + assertResourceOwners(t, clients[pusherName], tt.res.resourceOwners, tt.fields.aggregateIDs, tt.fields.aggregateType) + }) + } + } +} + +func pushAggregates(pusher eventstore.Pusher, aggregateCommands [][]eventstore.Command) []error { + wg := sync.WaitGroup{} + errs := make([]error, 0) + errsMu := sync.Mutex{} + wg.Add(len(aggregateCommands)) + + ctx, cancel := context.WithCancel(context.Background()) + + for _, commands := range aggregateCommands { + go func(events []eventstore.Command) { + <-ctx.Done() + + _, err := pusher.Push(context.Background(), events...) //nolint:contextcheck + if err != nil { + errsMu.Lock() + errs = append(errs, err) + errsMu.Unlock() + } + + wg.Done() + }(commands) + } + + // wait till all routines are started + time.Sleep(100 * time.Millisecond) + cancel() + wg.Wait() + + return errs +} + +func assertResourceOwners(t *testing.T, db *database.DB, resourceOwners, aggregateIDs database.TextArray[string], aggregateType string) { + t.Helper() + + eventCount := 0 + err := db.Query(func(rows *sql.Rows) error { + for i := 0; rows.Next(); i++ { + var resourceOwner string + err := rows.Scan(&resourceOwner) + if err != nil { + return err + } + if resourceOwner != resourceOwners[i] { + t.Errorf("unexpected resource owner in queried event. want %q, got: %q", resourceOwners[i], resourceOwner) + } + eventCount++ + } + return nil + }, "SELECT owner FROM eventstore.events2 WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY position, in_tx_order", aggregateType, aggregateIDs) + if err != nil { + t.Error("query failed: ", err) + return + } + + if eventCount != len(resourceOwners) { + t.Errorf("wrong queried event count: want %d, got %d", len(resourceOwners), eventCount) + } +} + +func assertEventCount(t *testing.T, db *database.DB, aggTypes database.TextArray[eventstore.AggregateType], aggIDs database.TextArray[string], maxPushedEventsCount int) { + t.Helper() + + var count int + err := db.QueryRow(func(row *sql.Row) error { + return row.Scan(&count) + }, "SELECT count(*) FROM eventstore.events2 where aggregate_type = ANY($1) AND aggregate_id = ANY($2)", aggTypes, aggIDs) + + if err != nil { + t.Errorf("unexpected err in row.Scan: %v", err) + return + } + + if count > maxPushedEventsCount { + t.Errorf("expected push count %d got %d", maxPushedEventsCount, count) + } +} + +func assertUniqueConstraint(t *testing.T, db *database.DB, commands []eventstore.Command, expectedCount int) { + t.Helper() + + var uniqueConstraint *eventstore.UniqueConstraint + for _, command := range commands { + if e := command.(*testEvent); len(e.uniqueConstraints) > 0 { + uniqueConstraint = e.uniqueConstraints[0] + break + } + } + if uniqueConstraint == nil { + return + } + + var uniqueCount int + err := db.QueryRow(func(row *sql.Row) error { + return row.Scan(&uniqueCount) + }, "SELECT COUNT(*) FROM eventstore.unique_constraints where unique_type = $1 AND unique_field = $2", uniqueConstraint.UniqueType, uniqueConstraint.UniqueField) + if err != nil { + t.Error("unable to query inserted rows: ", err) + return + } + if uniqueCount != expectedCount { + t.Errorf("expected unique count %d got %d", expectedCount, uniqueCount) + } +} diff --git a/internal/eventstore/eventstore_querier_test.go b/internal/eventstore/eventstore_querier_test.go new file mode 100644 index 000000000..856bb4a20 --- /dev/null +++ b/internal/eventstore/eventstore_querier_test.go @@ -0,0 +1,182 @@ +package eventstore_test + +import ( + "context" + "testing" + + "github.com/zitadel/zitadel/internal/eventstore" +) + +func TestCRDB_Filter(t *testing.T) { + type args struct { + searchQuery *eventstore.SearchQueryBuilder + } + type fields struct { + existingEvents []eventstore.Command + } + type res struct { + eventCount int + } + tests := []struct { + name string + fields fields + args args + res res + wantErr bool + }{ + { + name: "aggregate type filter no events", + args: args{ + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + AggregateTypes("not found"). + Builder(), + }, + fields: fields{ + existingEvents: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "300"), + generateCommand(eventstore.AggregateType(t.Name()), "300"), + generateCommand(eventstore.AggregateType(t.Name()), "300"), + }, + }, + res: res{ + eventCount: 0, + }, + wantErr: false, + }, + { + name: "aggregate type and id filter events found", + args: args{ + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + AggregateTypes(eventstore.AggregateType(t.Name())). + AggregateIDs("303"). + Builder(), + }, + fields: fields{ + existingEvents: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "303"), + generateCommand(eventstore.AggregateType(t.Name()), "303"), + generateCommand(eventstore.AggregateType(t.Name()), "303"), + generateCommand(eventstore.AggregateType(t.Name()), "305"), + }, + }, + res: res{ + eventCount: 3, + }, + wantErr: false, + }, + } + for _, tt := range tests { + for querierName, querier := range queriers { + t.Run(querierName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[querierName])) + + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: querier, + Pusher: pushers["v3(inmemory)"], + }, + ) + + // setup initial data for query + if _, err := db.Push(context.Background(), tt.fields.existingEvents...); err != nil { + t.Errorf("error in setup = %v", err) + return + } + + events, err := db.Filter(context.Background(), tt.args.searchQuery) + if (err != nil) != tt.wantErr { + t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr) + } + + if len(events) != tt.res.eventCount { + t.Errorf("CRDB.query() expected event count: %d got %d", tt.res.eventCount, len(events)) + } + }) + } + } +} + +func TestCRDB_LatestSequence(t *testing.T) { + type args struct { + searchQuery *eventstore.SearchQueryBuilder + } + type fields struct { + existingEvents []eventstore.Command + } + type res struct { + sequence float64 + } + tests := []struct { + name string + fields fields + args args + res res + wantErr bool + }{ + { + name: "aggregate type filter no sequence", + args: args{ + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence). + AddQuery(). + AggregateTypes("not found"). + Builder(), + }, + fields: fields{ + existingEvents: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "400"), + generateCommand(eventstore.AggregateType(t.Name()), "400"), + generateCommand(eventstore.AggregateType(t.Name()), "400"), + }, + }, + wantErr: false, + }, + { + name: "aggregate type filter sequence", + args: args{ + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxSequence). + AddQuery(). + AggregateTypes(eventstore.AggregateType(t.Name())). + Builder(), + }, + fields: fields{ + existingEvents: []eventstore.Command{ + generateCommand(eventstore.AggregateType(t.Name()), "401"), + generateCommand(eventstore.AggregateType(t.Name()), "401"), + generateCommand(eventstore.AggregateType(t.Name()), "401"), + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + for querierName, querier := range queriers { + t.Run(querierName+"/"+tt.name, func(t *testing.T) { + t.Cleanup(cleanupEventstore(clients[querierName])) + + db := eventstore.NewEventstore( + &eventstore.Config{ + Querier: querier, + Pusher: pushers["v3(inmemory)"], + }, + ) + + // setup initial data for query + _, err := db.Push(context.Background(), tt.fields.existingEvents...) + if err != nil { + t.Errorf("error in setup = %v", err) + return + } + + sequence, err := db.LatestSequence(context.Background(), tt.args.searchQuery) + if (err != nil) != tt.wantErr { + t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.res.sequence > sequence { + t.Errorf("CRDB.query() expected sequence: %v got %v", tt.res.sequence, sequence) + } + }) + } + } +} diff --git a/internal/eventstore/eventstore_test.go b/internal/eventstore/eventstore_test.go index e9054696e..8738a47af 100644 --- a/internal/eventstore/eventstore_test.go +++ b/internal/eventstore/eventstore_test.go @@ -2,16 +2,16 @@ package eventstore import ( "context" - "database/sql" + "encoding/json" "fmt" "reflect" "sync" "testing" + "time" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/service" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) // testEvent implements the Event interface @@ -36,11 +36,11 @@ func newTestEvent(id, description string, data func() interface{}, checkPrevious } } -func (e *testEvent) Data() interface{} { +func (e *testEvent) Payload() interface{} { return e.data() } -func (e *testEvent) UniqueConstraints() []*EventUniqueConstraint { +func (e *testEvent) UniqueConstraints() []*UniqueConstraint { return nil } @@ -48,7 +48,7 @@ func (e *testEvent) Assets() []*Asset { return nil } -func testFilterMapper(event *repository.Event) (Event, error) { +func testFilterMapper(event Event) (Event, error) { if event == nil { return newTestEvent("testID", "hodor", nil, false), nil } @@ -61,7 +61,7 @@ func Test_eventstore_RegisterFilterEventMapper(t *testing.T) { } type args struct { eventType EventType - mapper func(*repository.Event) (Event, error) + mapper func(Event) (Event, error) } type res struct { event Event @@ -74,19 +74,6 @@ func Test_eventstore_RegisterFilterEventMapper(t *testing.T) { args args res res }{ - { - name: "no event type", - args: args{ - eventType: "", - mapper: testFilterMapper, - }, - fields: fields{ - eventMapper: map[EventType]eventTypeInterceptors{}, - }, - res: res{ - mapperCount: 0, - }, - }, { name: "no event mapper", args: args{ @@ -135,7 +122,7 @@ func Test_eventstore_RegisterFilterEventMapper(t *testing.T) { fields: fields{ eventMapper: map[EventType]eventTypeInterceptors{ "event.type": { - eventMapper: func(*repository.Event) (Event, error) { + eventMapper: func(Event) (Event, error) { return nil, errors.ThrowUnimplemented(nil, "V2-1qPvn", "unimplemented") }, }, @@ -342,401 +329,94 @@ func Test_eventData(t *testing.T) { } } -func TestEventstore_aggregatesToEvents(t *testing.T) { - type args struct { - instanceID string - events []Command - } - type res struct { - wantErr bool - events []*repository.Event - } - tests := []struct { - name string - args args - res res - }{ - { - name: "one aggregate one event", - args: args{ - instanceID: "instanceID", - events: []Command{ - newTestEvent( - "1", - "", - func() interface{} { - return nil - }, - false), - }, - }, - res: res{ - wantErr: false, - events: []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "instanceID", - Type: "test.event", - Version: "v1", - }, - }, - }, - }, - { - name: "one aggregate multiple events", - args: args{ - instanceID: "instanceID", - events: []Command{ - newTestEvent( - "1", - "", - func() interface{} { - return nil - }, - false), - newTestEvent( - "1", - "", - func() interface{} { - return nil - }, - false), - }, - }, - res: res{ - wantErr: false, - events: []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "instanceID", - Type: "test.event", - Version: "v1", - }, - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "instanceID", - Type: "test.event", - Version: "v1", - }, - }, - }, - }, - { - name: "invalid data", - args: args{ - instanceID: "instanceID", - events: []Command{ - newTestEvent( - "1", - "", - func() interface{} { - return `{"data":""` - }, - false), - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "no aggregate id", - args: args{ - events: []Command{ - &testEvent{ - BaseEvent: *NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "resourceOwner", "editorUser"), "editorService"), - NewAggregate( - authz.NewMockContext("zitadel", "caos", "adlerhurst"), - "", - "test.aggregate", - "v1", - ), - "test.event", - ), - data: func() interface{} { - return nil - }, - }, - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "no aggregate type", - args: args{ - events: []Command{ - &testEvent{ - BaseEvent: *NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "resourceOwner", "editorUser"), "editorService"), - NewAggregate( - authz.NewMockContext("zitadel", "caos", "adlerhurst"), - "id", - "", - "v1", - ), - "test.event", - ), - data: func() interface{} { - return nil - }, - }, - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "no aggregate version", - args: args{ - events: []Command{ - &testEvent{ - BaseEvent: *NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "resourceOwner", "editorUser"), "editorService"), - NewAggregate( - authz.NewMockContext("zitadel", "caos", "adlerhurst"), - "id", - "test.aggregate", - "", - ), - "test.event", - ), - data: func() interface{} { - return nil - }, - }, - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "no event type", - args: args{ - events: []Command{ - &testEvent{ - BaseEvent: *NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "resourceOwner", "editorUser"), "editorService"), - NewAggregate( - authz.NewMockContext("zitadel", "caos", "adlerhurst"), - "id", - "test.aggregate", - "v1", - ), - "", - ), - data: func() interface{} { - return nil - }, - }, - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "no resourceowner", - args: args{ - events: []Command{ - &testEvent{ - BaseEvent: *NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "", "editorUser"), "editorService"), - NewAggregate( - authz.NewMockContext("zitadel", "", "adlerhurst"), - "id", - "test.aggregate", - "v1", - ), - "test.event", - ), - data: func() interface{} { - return nil - }, - }, - }, - }, - res: res{ - wantErr: false, - events: []*repository.Event{ - { - AggregateID: "id", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "", Valid: false}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", - }, - }, - }, - }, - { - name: "multiple aggregates", - args: args{ - events: []Command{ - newTestEvent( - "1", - "", - func() interface{} { - return nil - }, - false), - newTestEvent( - "1", - "", - func() interface{} { - return nil - }, - false), - newTestEvent( - "2", - "", - func() interface{} { - return nil - }, - true), - }, - }, - res: res{ - wantErr: false, - events: combineEventLists( - []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", - }, - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", - }, - }, - []*repository.Event{ - { - AggregateID: "2", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", - }, - }, - ), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - events, _, err := commandsToRepository(tt.args.instanceID, tt.args.events) - if (err != nil) != tt.res.wantErr { - t.Errorf("Eventstore.aggregatesToEvents() error = %v, wantErr %v", err, tt.res.wantErr) - return - } +type testPusher struct { + events []Event + err error - if err != nil { - return - } - - if len(tt.res.events) != len(events) { - t.Errorf("length of events unequal want: %d got %d", len(tt.res.events), len(events)) - return - } - - for i := 0; i < len(tt.res.events); i++ { - compareEvents(t, tt.res.events[i], events[i]) - } - }) - } + t *testing.T } -type testRepo struct { - events []*repository.Event - sequence uint64 +func (repo *testPusher) Health(ctx context.Context) error { + return nil +} + +func (repo *testPusher) Push(ctx context.Context, commands ...Command) (events []Event, err error) { + if repo.err != nil { + return nil, repo.err + } + + if len(repo.events) != len(commands) { + repo.t.Errorf("length of events unequal want: %d got %d", len(repo.events), len(commands)) + return nil, fmt.Errorf("") + } + + events = make([]Event, len(commands)) + + for i := 0; i < len(repo.events); i++ { + + var payload []byte + switch p := commands[i].Payload().(type) { + case []byte: + payload = p + case nil: + // don't do anything + default: + payload, err = json.Marshal(p) + if err != nil { + return nil, err + } + } + + compareEvents(repo.t, repo.events[i], commands[i]) + events[i] = &BaseEvent{ + Seq: uint64(i), + Creation: time.Now(), + EventType: commands[i].Type(), + Data: payload, + User: commands[i].Creator(), + Agg: &Aggregate{ + Version: commands[i].Aggregate().Version, + ID: commands[i].Aggregate().ID, + Type: commands[i].Aggregate().Type, + ResourceOwner: commands[i].Aggregate().ResourceOwner, + InstanceID: commands[i].Aggregate().InstanceID, + }, + } + } + + return events, nil +} + +type testQuerier struct { + events []Event + sequence float64 instances []string err error t *testing.T } -func (repo *testRepo) Health(ctx context.Context) error { +func (repo *testQuerier) Health(ctx context.Context) error { return nil } -func (repo *testRepo) CreateInstance(ctx context.Context, instance string) error { +func (repo *testQuerier) CreateInstance(ctx context.Context, instance string) error { return nil } - -func (repo *testRepo) Step20(context.Context, uint64) error { return nil } - -func (repo *testRepo) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - if repo.err != nil { - return repo.err - } - - if len(repo.events) != len(events) { - repo.t.Errorf("length of events unequal want: %d got %d", len(repo.events), len(events)) - return fmt.Errorf("") - } - - for i := 0; i < len(repo.events); i++ { - compareEvents(repo.t, repo.events[i], events[i]) - } - - return nil -} - -func (repo *testRepo) Filter(ctx context.Context, searchQuery *repository.SearchQuery) ([]*repository.Event, error) { +func (repo *testQuerier) Filter(ctx context.Context, searchQuery *SearchQueryBuilder) ([]Event, error) { if repo.err != nil { return nil, repo.err } return repo.events, nil } -func (repo *testRepo) LatestSequence(ctx context.Context, queryFactory *repository.SearchQuery) (uint64, error) { +func (repo *testQuerier) LatestSequence(ctx context.Context, queryFactory *SearchQueryBuilder) (float64, error) { if repo.err != nil { return 0, repo.err } return repo.sequence, nil } -func (repo *testRepo) InstanceIDs(ctx context.Context, queryFactory *repository.SearchQuery) ([]string, error) { +func (repo *testQuerier) InstanceIDs(ctx context.Context, queryFactory *SearchQueryBuilder) ([]string, error) { if repo.err != nil { return nil, repo.err } @@ -748,8 +428,8 @@ func TestEventstore_Push(t *testing.T) { events []Command } type fields struct { - repo *testRepo - eventMapper map[EventType]func(*repository.Event) (Event, error) + pusher *testPusher + eventMapper map[EventType]func(Event) (Event, error) } type res struct { wantErr bool @@ -768,31 +448,37 @@ func TestEventstore_Push(t *testing.T) { "1", "", func() interface{} { - return nil + return []byte(nil) }, false), }, }, fields: fields{ - repo: &testRepo{ + pusher: &testPusher{ t: t, - events: []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + events: []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "1", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, }, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { - return &testEvent{}, nil + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { + return &testEvent{ + BaseEvent: BaseEvent{ + Agg: &Aggregate{ + Type: e.Aggregate().Type, + }, + }, + }, nil }, }, }, @@ -805,49 +491,55 @@ func TestEventstore_Push(t *testing.T) { "1", "", func() interface{} { - return nil + return []byte(nil) }, false), newTestEvent( "1", "", func() interface{} { - return nil + return []byte(nil) }, false), }, }, fields: fields{ - repo: &testRepo{ + pusher: &testPusher{ t: t, - events: []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + events: []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "1", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + &BaseEvent{ + Agg: &Aggregate{ + ID: "1", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, }, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { - return &testEvent{}, nil + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { + return &testEvent{ + BaseEvent: BaseEvent{ + Agg: &Aggregate{ + Type: e.Aggregate().Type, + }, + }, + }, nil }, }, }, @@ -863,71 +555,77 @@ func TestEventstore_Push(t *testing.T) { "1", "", func() interface{} { - return nil + return []byte(nil) }, false), newTestEvent( "1", "", func() interface{} { - return nil + return []byte(nil) }, false), newTestEvent( "2", "", func() interface{} { - return nil + return []byte(nil) }, true), }, }, fields: fields{ - repo: &testRepo{ + pusher: &testPusher{ t: t, events: combineEventLists( - []*repository.Event{ - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "1", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, - { - AggregateID: "1", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + &BaseEvent{ + Agg: &Aggregate{ + ID: "1", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, }, - []*repository.Event{ - { - AggregateID: "2", - AggregateType: "test.aggregate", - Data: []byte(nil), - EditorService: "editorService", - EditorUser: "editorUser", - ResourceOwner: sql.NullString{String: "caos", Valid: true}, - InstanceID: "zitadel", - Type: "test.event", - Version: "v1", + []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "2", + Type: "test.aggregate", + ResourceOwner: "caos", + InstanceID: "zitadel", + }, + Data: []byte(nil), + User: "editorUser", + EventType: "test.event", }, }, ), }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { - return &testEvent{}, nil + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { + return &testEvent{ + BaseEvent: BaseEvent{ + Agg: &Aggregate{ + Type: e.Aggregate().Type, + }, + }, + }, nil }, }, }, @@ -943,13 +641,13 @@ func TestEventstore_Push(t *testing.T) { "1", "", func() interface{} { - return nil + return []byte(nil) }, false), }, }, fields: fields{ - repo: &testRepo{ + pusher: &testPusher{ t: t, err: errors.ThrowInternal(nil, "V2-qaa4S", "test err"), }, @@ -972,7 +670,7 @@ func TestEventstore_Push(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ + pusher: &testPusher{ t: t, err: errors.ThrowInternal(nil, "V2-qaa4S", "test err"), }, @@ -985,7 +683,7 @@ func TestEventstore_Push(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { es := &Eventstore{ - repo: tt.fields.repo, + pusher: tt.fields.pusher, interceptorMutex: sync.Mutex{}, eventInterceptors: map[EventType]eventTypeInterceptors{}, } @@ -1010,8 +708,8 @@ func TestEventstore_FilterEvents(t *testing.T) { query *SearchQueryBuilder } type fields struct { - repo *testRepo - eventMapper map[EventType]func(*repository.Event) (Event, error) + repo *testQuerier + eventMapper map[EventType]func(Event) (Event, error) } type res struct { wantErr bool @@ -1022,20 +720,11 @@ func TestEventstore_FilterEvents(t *testing.T) { fields fields res res }{ - { - name: "invalid factory", - args: args{ - query: nil, - }, - res: res{ - wantErr: true, - }, - }, { name: "no events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1045,12 +734,12 @@ func TestEventstore_FilterEvents(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{}, + repo: &testQuerier{ + events: []Event{}, t: t, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1063,7 +752,7 @@ func TestEventstore_FilterEvents(t *testing.T) { name: "repo error", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1073,12 +762,12 @@ func TestEventstore_FilterEvents(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ + repo: &testQuerier{ t: t, err: errors.ThrowInternal(nil, "V2-RfkBa", "test err"), }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1091,7 +780,7 @@ func TestEventstore_FilterEvents(t *testing.T) { name: "found events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1101,17 +790,19 @@ func TestEventstore_FilterEvents(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{ - { - AggregateID: "test.aggregate", - Type: "test.event", + repo: &testQuerier{ + events: []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "test.aggregate", + }, + EventType: "test.event", }, }, t: t, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1124,7 +815,7 @@ func TestEventstore_FilterEvents(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { es := &Eventstore{ - repo: tt.fields.repo, + querier: tt.fields.repo, interceptorMutex: sync.Mutex{}, eventInterceptors: map[EventType]eventTypeInterceptors{}, } @@ -1150,7 +841,7 @@ func TestEventstore_LatestSequence(t *testing.T) { query *SearchQueryBuilder } type fields struct { - repo *testRepo + repo *testQuerier } type res struct { wantErr bool @@ -1161,20 +852,11 @@ func TestEventstore_LatestSequence(t *testing.T) { fields fields res res }{ - { - name: "invalid factory", - args: args{ - query: nil, - }, - res: res{ - wantErr: true, - }, - }, { name: "no events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsMaxSequence, + columns: ColumnsMaxSequence, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1184,8 +866,8 @@ func TestEventstore_LatestSequence(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{}, + repo: &testQuerier{ + events: []Event{}, t: t, }, }, @@ -1197,7 +879,7 @@ func TestEventstore_LatestSequence(t *testing.T) { name: "repo error", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsMaxSequence, + columns: ColumnsMaxSequence, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1207,7 +889,7 @@ func TestEventstore_LatestSequence(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ + repo: &testQuerier{ t: t, err: errors.ThrowInternal(nil, "V2-RfkBa", "test err"), }, @@ -1220,7 +902,7 @@ func TestEventstore_LatestSequence(t *testing.T) { name: "found events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsMaxSequence, + columns: ColumnsMaxSequence, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1230,9 +912,9 @@ func TestEventstore_LatestSequence(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - sequence: 50, - t: t, + repo: &testQuerier{ + // sequence: time.Now(), + t: t, }, }, res: res{ @@ -1243,7 +925,7 @@ func TestEventstore_LatestSequence(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { es := &Eventstore{ - repo: tt.fields.repo, + querier: tt.fields.repo, } _, err := es.LatestSequence(context.Background(), tt.args.query) @@ -1282,8 +964,8 @@ func TestEventstore_FilterToReducer(t *testing.T) { readModel reducer } type fields struct { - repo *testRepo - eventMapper map[EventType]func(*repository.Event) (Event, error) + repo *testQuerier + eventMapper map[EventType]func(Event) (Event, error) } type res struct { wantErr bool @@ -1294,20 +976,11 @@ func TestEventstore_FilterToReducer(t *testing.T) { fields fields res res }{ - { - name: "invalid factory", - args: args{ - query: nil, - }, - res: res{ - wantErr: true, - }, - }, { name: "no events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1321,12 +994,12 @@ func TestEventstore_FilterToReducer(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{}, + repo: &testQuerier{ + events: []Event{}, t: t, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1339,7 +1012,7 @@ func TestEventstore_FilterToReducer(t *testing.T) { name: "repo error", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1353,12 +1026,12 @@ func TestEventstore_FilterToReducer(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ + repo: &testQuerier{ t: t, err: errors.ThrowInternal(nil, "V2-RfkBa", "test err"), }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1371,7 +1044,7 @@ func TestEventstore_FilterToReducer(t *testing.T) { name: "found events", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1385,17 +1058,19 @@ func TestEventstore_FilterToReducer(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{ - { - AggregateID: "test.aggregate", - Type: "test.event", + repo: &testQuerier{ + events: []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "test.aggregate", + }, + EventType: "test.event", }, }, t: t, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1405,7 +1080,7 @@ func TestEventstore_FilterToReducer(t *testing.T) { name: "append in reducer fails", args: args{ query: &SearchQueryBuilder{ - columns: repository.ColumnsEvent, + columns: ColumnsEvent, queries: []*SearchQuery{ { builder: &SearchQueryBuilder{}, @@ -1420,17 +1095,19 @@ func TestEventstore_FilterToReducer(t *testing.T) { }, }, fields: fields{ - repo: &testRepo{ - events: []*repository.Event{ - { - AggregateID: "test.aggregate", - Type: "test.event", + repo: &testQuerier{ + events: []Event{ + &BaseEvent{ + Agg: &Aggregate{ + ID: "test.aggregate", + }, + EventType: "test.event", }, }, t: t, }, - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(e *repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(e Event) (Event, error) { return &testEvent{}, nil }, }, @@ -1443,7 +1120,7 @@ func TestEventstore_FilterToReducer(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { es := &Eventstore{ - repo: tt.fields.repo, + querier: tt.fields.repo, interceptorMutex: sync.Mutex{}, eventInterceptors: map[EventType]eventTypeInterceptors{}, } @@ -1463,52 +1140,46 @@ func TestEventstore_FilterToReducer(t *testing.T) { } } -func combineEventLists(lists ...[]*repository.Event) []*repository.Event { - events := []*repository.Event{} +func combineEventLists(lists ...[]Event) []Event { + events := []Event{} for _, list := range lists { events = append(events, list...) } return events } -func compareEvents(t *testing.T, want, got *repository.Event) { +func compareEvents(t *testing.T, want Event, got Command) { t.Helper() - if want.AggregateID != got.AggregateID { - t.Errorf("wrong aggregateID got %q want %q", got.AggregateID, want.AggregateID) + if want.Aggregate().ID != got.Aggregate().ID { + t.Errorf("wrong aggregateID got %q want %q", got.Aggregate().ID, want.Aggregate().ID) } - if want.AggregateType != got.AggregateType { - t.Errorf("wrong aggregateType got %q want %q", got.AggregateType, want.AggregateType) + if want.Aggregate().Type != got.Aggregate().Type { + t.Errorf("wrong aggregateType got %q want %q", got.Aggregate().Type, want.Aggregate().Type) } - if !reflect.DeepEqual(want.Data, got.Data) { - t.Errorf("wrong data got %s want %s", string(got.Data), string(want.Data)) + if !reflect.DeepEqual(want.DataAsBytes(), got.Payload()) { + t.Errorf("wrong data got %s want %s", string(got.Payload().([]byte)), string(want.DataAsBytes())) } - if want.EditorService != got.EditorService { - t.Errorf("wrong editor service got %q want %q", got.EditorService, want.EditorService) + if want.Creator() != got.Creator() { + t.Errorf("wrong creator got %q want %q", got.Creator(), want.Creator()) } - if want.EditorUser != got.EditorUser { - t.Errorf("wrong editor user got %q want %q", got.EditorUser, want.EditorUser) + if want.Aggregate().ResourceOwner != got.Aggregate().ResourceOwner { + t.Errorf("wrong resource owner got %q want %q", got.Aggregate().ResourceOwner, want.Aggregate().ResourceOwner) } - if want.ResourceOwner != got.ResourceOwner { - t.Errorf("wrong resource owner got %q want %q", got.ResourceOwner.String, want.ResourceOwner.String) + if want.Type() != got.Type() { + t.Errorf("wrong event type got %q want %q", got.Type(), want.Type()) } - if want.Type != got.Type { - t.Errorf("wrong event type got %q want %q", got.Type, want.Type) - } - if want.Version != got.Version { - t.Errorf("wrong version got %q want %q", got.Version, want.Version) - } - if want.PreviousAggregateSequence != got.PreviousAggregateSequence { - t.Errorf("wrong previous sequence got %d want %d", got.PreviousAggregateSequence, want.PreviousAggregateSequence) + if want.Revision() != got.Revision() { + t.Errorf("wrong version got %q want %q", got.Revision(), want.Revision()) } } func TestEventstore_mapEvents(t *testing.T) { type fields struct { - eventMapper map[EventType]func(*repository.Event) (Event, error) + eventMapper map[EventType]func(Event) (Event, error) } type args struct { - events []*repository.Event + events []Event } type res struct { events []Event @@ -1520,36 +1191,18 @@ func TestEventstore_mapEvents(t *testing.T) { args args res res }{ - //{ - // name: "no mapper", - // args: args{ - // events: []*repository.Event{ - // { - // Type: "no.mapper.found", - // }, - // }, - // }, - // fields: fields{ - // eventMapper: map[EventType]func(*repository.Event) (EventReader, error){}, - // }, - // res: res{ - // //TODO: as long as not all events are implemented in v2 eventstore doesn't return an error - // // afterwards it will return an error on un - // wantErr: true, - // }, - //}, { name: "mapping failed", args: args{ - events: []*repository.Event{ - { - Type: "test.event", + events: []Event{ + &BaseEvent{ + EventType: "test.event", }, }, }, fields: fields{ - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(*repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(Event) (Event, error) { return nil, errors.ThrowInternal(nil, "V2-8FbQk", "test err") }, }, @@ -1561,15 +1214,15 @@ func TestEventstore_mapEvents(t *testing.T) { { name: "mapping succeeded", args: args{ - events: []*repository.Event{ - { - Type: "test.event", + events: []Event{ + &BaseEvent{ + EventType: "test.event", }, }, }, fields: fields{ - eventMapper: map[EventType]func(*repository.Event) (Event, error){ - "test.event": func(*repository.Event) (Event, error) { + eventMapper: map[EventType]func(Event) (Event, error){ + "test.event": func(Event) (Event, error) { return &testEvent{}, nil }, }, diff --git a/internal/eventstore/example_test.go b/internal/eventstore/example_test.go index 4e4e849a5..c1051f826 100644 --- a/internal/eventstore/example_test.go +++ b/internal/eventstore/example_test.go @@ -2,13 +2,13 @@ package eventstore_test import ( "context" - "encoding/json" "testing" "time" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" + query_repo "github.com/zitadel/zitadel/internal/eventstore/repository/sql" + v3 "github.com/zitadel/zitadel/internal/eventstore/v3" ) // ------------------------------------------------------------ @@ -43,12 +43,13 @@ func NewUserAddedEvent(id string, firstName string) *UserAddedEvent { } } -func UserAddedEventMapper() (eventstore.AggregateType, eventstore.EventType, func(*repository.Event) (eventstore.Event, error)) { - return "user", "user.added", func(event *repository.Event) (eventstore.Event, error) { +func UserAddedEventMapper() (eventstore.AggregateType, eventstore.EventType, func(eventstore.Event) (eventstore.Event, error)) { + return "user", "user.added", func(event eventstore.Event) (eventstore.Event, error) { e := &UserAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + + err := event.Unmarshal(e) if err != nil { return nil, err } @@ -56,11 +57,11 @@ func UserAddedEventMapper() (eventstore.AggregateType, eventstore.EventType, fun } } -func (e *UserAddedEvent) Data() interface{} { +func (e *UserAddedEvent) Payload() interface{} { return e } -func (e *UserAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -88,12 +89,12 @@ func NewUserFirstNameChangedEvent(id, firstName string) *UserFirstNameChangedEve } } -func UserFirstNameChangedMapper() (eventstore.AggregateType, eventstore.EventType, func(*repository.Event) (eventstore.Event, error)) { - return "user", "user.firstName.changed", func(event *repository.Event) (eventstore.Event, error) { +func UserFirstNameChangedMapper() (eventstore.AggregateType, eventstore.EventType, func(eventstore.Event) (eventstore.Event, error)) { + return "user", "user.firstName.changed", func(event eventstore.Event) (eventstore.Event, error) { e := &UserFirstNameChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, err } @@ -101,11 +102,11 @@ func UserFirstNameChangedMapper() (eventstore.AggregateType, eventstore.EventTyp } } -func (e *UserFirstNameChangedEvent) Data() interface{} { +func (e *UserFirstNameChangedEvent) Payload() interface{} { return e } -func (e *UserFirstNameChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserFirstNameChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -130,19 +131,19 @@ func NewUserPasswordCheckedEvent(id string) *UserPasswordCheckedEvent { } } -func UserPasswordCheckedMapper() (eventstore.AggregateType, eventstore.EventType, func(*repository.Event) (eventstore.Event, error)) { - return "user", "user.password.checked", func(event *repository.Event) (eventstore.Event, error) { +func UserPasswordCheckedMapper() (eventstore.AggregateType, eventstore.EventType, func(eventstore.Event) (eventstore.Event, error)) { + return "user", "user.password.checked", func(event eventstore.Event) (eventstore.Event, error) { return &UserPasswordCheckedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } } -func (e *UserPasswordCheckedEvent) Data() interface{} { +func (e *UserPasswordCheckedEvent) Payload() interface{} { return nil } -func (e *UserPasswordCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserPasswordCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -167,19 +168,19 @@ func NewUserDeletedEvent(id string) *UserDeletedEvent { } } -func UserDeletedMapper() (eventstore.AggregateType, eventstore.EventType, func(*repository.Event) (eventstore.Event, error)) { - return "user", "user.deleted", func(event *repository.Event) (eventstore.Event, error) { +func UserDeletedMapper() (eventstore.AggregateType, eventstore.EventType, func(eventstore.Event) (eventstore.Event, error)) { + return "user", "user.deleted", func(event eventstore.Event) (eventstore.Event, error) { return &UserDeletedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } } -func (e *UserDeletedEvent) Data() interface{} { +func (e *UserDeletedEvent) Payload() interface{} { return nil } -func (e *UserDeletedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserDeletedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -286,12 +287,13 @@ func (rm *UserReadModel) Reduce() error { // ------------------------------------------------------------ func TestUserReadModel(t *testing.T) { - es, err := eventstore.Start(&eventstore.Config{Client: testCRDBClient}) - if err != nil { - t.Errorf("unable to start eventstore: %v", err) - t.FailNow() - } - // es := eventstore.NewEventstore(&eventstore.Config{re}) + es := eventstore.NewEventstore( + &eventstore.Config{ + Querier: query_repo.NewCRDB(testCRDBClient), + Pusher: v3.NewEventstore(testCRDBClient), + }, + ) + es.RegisterFilterEventMapper(UserAddedEventMapper()). RegisterFilterEventMapper(UserFirstNameChangedMapper()). RegisterFilterEventMapper(UserPasswordCheckedMapper()). diff --git a/internal/eventstore/handler/crdb/current_sequence.go b/internal/eventstore/handler/crdb/current_sequence.go deleted file mode 100644 index 2b92343be..000000000 --- a/internal/eventstore/handler/crdb/current_sequence.go +++ /dev/null @@ -1,83 +0,0 @@ -package crdb - -import ( - "context" - "database/sql" - "strconv" - "strings" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" -) - -const ( - currentSequenceStmtFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2) FOR UPDATE` - currentSequenceStmtWithoutLockFormat = `SELECT current_sequence, aggregate_type, instance_id FROM %s WHERE projection_name = $1 AND instance_id = ANY ($2)` - updateCurrentSequencesStmtFormat = `INSERT INTO %s (projection_name, aggregate_type, current_sequence, instance_id, timestamp) VALUES ` - updateCurrentSequencesConflictStmt = ` ON CONFLICT (projection_name, aggregate_type, instance_id) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp` -) - -type currentSequences map[eventstore.AggregateType][]*instanceSequence - -type instanceSequence struct { - instanceID string - sequence uint64 -} - -func (h *StatementHandler) currentSequences(ctx context.Context, isTx bool, query func(context.Context, func(*sql.Rows) error, string, ...interface{}) error, instanceIDs database.StringArray) (currentSequences, error) { - stmt := h.currentSequenceStmt - if !isTx { - stmt = h.currentSequenceWithoutLockStmt - } - - sequences := make(currentSequences, len(h.aggregates)) - err := query(ctx, - func(rows *sql.Rows) error { - for rows.Next() { - var ( - aggregateType eventstore.AggregateType - sequence uint64 - instanceID string - ) - - err := rows.Scan(&sequence, &aggregateType, &instanceID) - if err != nil { - return errors.ThrowInternal(err, "CRDB-dbatK", "scan failed") - } - - sequences[aggregateType] = append(sequences[aggregateType], &instanceSequence{ - sequence: sequence, - instanceID: instanceID, - }) - } - return nil - }, - stmt, h.ProjectionName, instanceIDs) - if err != nil { - return nil, err - } - return sequences, nil -} - -func (h *StatementHandler) updateCurrentSequences(tx *sql.Tx, sequences currentSequences) error { - valueQueries := make([]string, 0, len(sequences)) - valueCounter := 0 - values := make([]interface{}, 0, len(sequences)*3) - for aggregate, instanceSequence := range sequences { - for _, sequence := range instanceSequence { - valueQueries = append(valueQueries, "($"+strconv.Itoa(valueCounter+1)+", $"+strconv.Itoa(valueCounter+2)+", $"+strconv.Itoa(valueCounter+3)+", $"+strconv.Itoa(valueCounter+4)+", NOW())") - valueCounter += 4 - values = append(values, h.ProjectionName, aggregate, sequence.sequence, sequence.instanceID) - } - } - - res, err := tx.Exec(h.updateSequencesBaseStmt+strings.Join(valueQueries, ", ")+updateCurrentSequencesConflictStmt, values...) - if err != nil { - return errors.ThrowInternal(err, "CRDB-TrH2Z", "unable to exec update sequence") - } - if rows, _ := res.RowsAffected(); rows < 1 { - return errSeqNotUpdated - } - return nil -} diff --git a/internal/eventstore/handler/crdb/db_mock_test.go b/internal/eventstore/handler/crdb/db_mock_test.go index 20b70cf5e..aab63a414 100644 --- a/internal/eventstore/handler/crdb/db_mock_test.go +++ b/internal/eventstore/handler/crdb/db_mock_test.go @@ -1,301 +1,15 @@ package crdb import ( - "database/sql" "database/sql/driver" - "strings" - "testing" "time" "github.com/DATA-DOG/go-sqlmock" - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" ) type mockExpectation func(sqlmock.Sqlmock) -func expectFailureCount(tableName string, projectionName, instanceID string, failedSeq, failureCount uint64) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectQuery(`WITH failures AS \(SELECT failure_count FROM `+tableName+` WHERE projection_name = \$1 AND failed_sequence = \$2 AND instance_id = \$3\) SELECT COALESCE\(\(SELECT failure_count FROM failures\), 0\) AS failure_count`). - WithArgs(projectionName, failedSeq, instanceID). - WillReturnRows( - sqlmock.NewRows([]string{"failure_count"}). - AddRow(failureCount), - ) - } -} - -func expectUpdateFailureCount(tableName string, projectionName, instanceID string, seq, failureCount uint64) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec(`INSERT INTO `+tableName+` \(projection_name, failed_sequence, failure_count, error, instance_id, last_failed\) VALUES \(\$1, \$2, \$3, \$4\, \$5\, \$6\) ON CONFLICT \(projection_name, failed_sequence, instance_id\) DO UPDATE SET failure_count = EXCLUDED\.failure_count, error = EXCLUDED\.error, last_failed = EXCLUDED\.last_failed`). - WithArgs(projectionName, seq, failureCount, sqlmock.AnyArg(), instanceID, "NOW()").WillReturnResult(sqlmock.NewResult(1, 1)) - } -} - -func expectCreate(projectionName string, columnNames, placeholders []string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - args := make([]driver.Value, len(columnNames)) - for i := 0; i < len(columnNames); i++ { - args[i] = sqlmock.AnyArg() - placeholders[i] = `\` + placeholders[i] - } - m.ExpectExec("INSERT INTO " + projectionName + ` \(` + strings.Join(columnNames, ", ") + `\) VALUES \(` + strings.Join(placeholders, ", ") + `\)`). - WithArgs(args...). - WillReturnResult(sqlmock.NewResult(1, 1)) - } -} - -func expectCreateErr(projectionName string, columnNames, placeholders []string, err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - args := make([]driver.Value, len(columnNames)) - for i := 0; i < len(columnNames); i++ { - args[i] = sqlmock.AnyArg() - placeholders[i] = `\` + placeholders[i] - } - m.ExpectExec("INSERT INTO " + projectionName + ` \(` + strings.Join(columnNames, ", ") + `\) VALUES \(` + strings.Join(placeholders, ", ") + `\)`). - WithArgs(args...). - WillReturnError(err) - } -} - -func expectBegin() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectBegin() - } -} - -func expectBeginErr(err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectBegin().WillReturnError(err) - } -} - -func expectCommit() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectCommit() - } -} - -func expectCommitErr(err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectCommit().WillReturnError(err) - } -} - -func expectRollback() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectRollback() - } -} - -func expectSavePoint() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("SAVEPOINT push_stmt"). - WillReturnResult(sqlmock.NewResult(1, 1)) - } -} - -func expectSavePointErr(err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("SAVEPOINT push_stmt"). - WillReturnError(err) - } -} - -func expectSavePointRollback() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("ROLLBACK TO SAVEPOINT push_stmt"). - WillReturnResult(sqlmock.NewResult(1, 1)) - } -} - -func expectSavePointRollbackErr(err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("ROLLBACK TO SAVEPOINT push_stmt"). - WillReturnError(err) - } -} - -func expectSavePointRelease() func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("RELEASE push_stmt"). - WillReturnResult(sqlmock.NewResult(1, 1)) - } -} - -func expectCurrentSequence(isTx bool, tableName, projection string, seq uint64, aggregateType string, instanceIDs []string) func(sqlmock.Sqlmock) { - rows := sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}) - for _, instanceID := range instanceIDs { - rows.AddRow(seq, aggregateType, instanceID) - } - return func(m sqlmock.Sqlmock) { - stmt := `SELECT current_sequence, aggregate_type, instance_id FROM ` + tableName + ` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\)` - if isTx { - stmt += " FOR UPDATE" - } - m.ExpectQuery(stmt). - WithArgs( - projection, - database.StringArray(instanceIDs), - ). - WillReturnRows( - rows, - ) - } -} - -func expectCurrentSequenceErr(isTx bool, tableName, projection string, instanceIDs []string, err error) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - stmt := `SELECT current_sequence, aggregate_type, instance_id FROM ` + tableName + ` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\)` - if isTx { - stmt += " FOR UPDATE" - } - m.ExpectQuery(stmt). - WithArgs( - projection, - database.StringArray(instanceIDs), - ). - WillReturnError(err) - } -} - -func expectCurrentSequenceNoRows(tableName, projection string, instanceIDs []string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). - WithArgs( - projection, - database.StringArray(instanceIDs), - ). - WillReturnRows( - sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}), - ) - } -} - -func expectCurrentSequenceScanErr(tableName, projection string, instanceIDs []string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectQuery(`SELECT current_sequence, aggregate_type, instance_id FROM `+tableName+` WHERE projection_name = \$1 AND instance_id = ANY \(\$2\) FOR UPDATE`). - WithArgs( - projection, - database.StringArray(instanceIDs), - ). - WillReturnRows( - sqlmock.NewRows([]string{"current_sequence", "aggregate_type", "instance_id"}). - RowError(0, sql.ErrTxDone). - AddRow(0, "agg", "instanceID"), - ) - } -} - -func expectUpdateCurrentSequence(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`). - WithArgs( - projection, - aggregateType, - seq, - instanceID, - ). - WillReturnResult( - sqlmock.NewResult(1, 1), - ) - } -} - -func expectUpdateThreeCurrentSequence(t *testing.T, tableName, projection string, sequences currentSequences) func(sqlmock.Sqlmock) { - args := make([][]interface{}, 0) - for aggregateType, instanceSequences := range sequences { - for _, sequence := range instanceSequences { - args = append(args, []interface{}{ - projection, - aggregateType, - sequence.sequence, - sequence.instanceID, - }) - } - } - matcher := ¤tSequenceMatcher{t: t, seq: args} - matchers := make([]driver.Value, len(args)*4) - for i := 0; i < len(args)*4; i++ { - matchers[i] = matcher - } - return func(m sqlmock.Sqlmock) { - m.ExpectExec("INSERT INTO " + tableName + ` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\), \(\$5, \$6, \$7, \$8, NOW\(\)\), \(\$9, \$10, \$11, \$12, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`). - WithArgs( - matchers..., - ). - WillReturnResult( - sqlmock.NewResult(1, 1), - ) - } -} - -type currentSequenceMatcher struct { - seq [][]interface{} - i int - t *testing.T -} - -func (m *currentSequenceMatcher) Match(value driver.Value) bool { - if m.i%4 == 0 { - m.i = 0 - } - left := make([]interface{}, 0, len(m.seq)) - for _, seq := range m.seq { - found := seq[m.i] - if found == nil { - continue - } - switch v := value.(type) { - case string: - if found == v || found == eventstore.AggregateType(v) { - seq[m.i] = nil - m.i++ - return true - } - case int64: - if found == uint64(v) { - seq[m.i] = nil - m.i++ - return true - } - } - left = append(left, found) - } - m.t.Errorf("expected: %v, possible left values: %v", value, left) - m.t.FailNow() - return false -} - -func expectUpdateCurrentSequenceErr(tableName, projection string, seq uint64, err error, aggregateType, instanceID string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`). - WithArgs( - projection, - aggregateType, - seq, - instanceID, - ). - WillReturnError(err) - } -} - -func expectUpdateCurrentSequenceNoRows(tableName, projection string, seq uint64, aggregateType, instanceID string) func(sqlmock.Sqlmock) { - return func(m sqlmock.Sqlmock) { - m.ExpectExec("INSERT INTO "+tableName+` \(projection_name, aggregate_type, current_sequence, instance_id, timestamp\) VALUES \(\$1, \$2, \$3, \$4, NOW\(\)\) ON CONFLICT \(projection_name, aggregate_type, instance_id\) DO UPDATE SET current_sequence = EXCLUDED.current_sequence, timestamp = EXCLUDED.timestamp`). - WithArgs( - projection, - aggregateType, - seq, - instanceID, - ). - WillReturnResult( - sqlmock.NewResult(0, 0), - ) - } -} - func expectLock(lockTable, workerName string, d time.Duration, instanceID string) func(sqlmock.Sqlmock) { return func(m sqlmock.Sqlmock) { m.ExpectExec(`INSERT INTO `+lockTable+ @@ -308,7 +22,7 @@ func expectLock(lockTable, workerName string, d time.Duration, instanceID string d, projectionName, instanceID, - database.StringArray{instanceID}, + database.TextArray[string]{instanceID}, ). WillReturnResult( sqlmock.NewResult(1, 1), @@ -329,7 +43,7 @@ func expectLockMultipleInstances(lockTable, workerName string, d time.Duration, projectionName, instanceID1, instanceID2, - database.StringArray{instanceID1, instanceID2}, + database.TextArray[string]{instanceID1, instanceID2}, ). WillReturnResult( sqlmock.NewResult(1, 1), @@ -349,7 +63,7 @@ func expectLockNoRows(lockTable, workerName string, d time.Duration, instanceID d, projectionName, instanceID, - database.StringArray{instanceID}, + database.TextArray[string]{instanceID}, ). WillReturnResult(driver.ResultNoRows) } @@ -367,7 +81,7 @@ func expectLockErr(lockTable, workerName string, d time.Duration, instanceID str d, projectionName, instanceID, - database.StringArray{instanceID}, + database.TextArray[string]{instanceID}, ). WillReturnError(err) } diff --git a/internal/eventstore/handler/crdb/failed_stmt.go b/internal/eventstore/handler/crdb/failed_stmt.go deleted file mode 100644 index b7b10844e..000000000 --- a/internal/eventstore/handler/crdb/failed_stmt.go +++ /dev/null @@ -1,51 +0,0 @@ -package crdb - -import ( - "database/sql" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/handler" -) - -const ( - setFailureCountStmtFormat = "INSERT INTO %s" + - " (projection_name, failed_sequence, failure_count, error, instance_id, last_failed)" + - " VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (projection_name, failed_sequence, instance_id)" + - " DO UPDATE SET failure_count = EXCLUDED.failure_count, error = EXCLUDED.error, last_failed = EXCLUDED.last_failed" - failureCountStmtFormat = "WITH failures AS (SELECT failure_count FROM %s WHERE projection_name = $1 AND failed_sequence = $2 AND instance_id = $3)" + - " SELECT COALESCE((SELECT failure_count FROM failures), 0) AS failure_count" -) - -func (h *StatementHandler) handleFailedStmt(tx *sql.Tx, stmt *handler.Statement, execErr error) (shouldContinue bool) { - failureCount, err := h.failureCount(tx, stmt.Sequence, stmt.InstanceID) - if err != nil { - logging.WithFields("projection", h.ProjectionName, "sequence", stmt.Sequence).WithError(err).Warn("unable to get failure count") - return false - } - failureCount += 1 - err = h.setFailureCount(tx, stmt.Sequence, failureCount, execErr, stmt.InstanceID) - logging.WithFields("projection", h.ProjectionName, "sequence", stmt.Sequence).OnError(err).Warn("unable to update failure count") - - return failureCount >= h.maxFailureCount -} - -func (h *StatementHandler) failureCount(tx *sql.Tx, seq uint64, instanceID string) (count uint, err error) { - row := tx.QueryRow(h.failureCountStmt, h.ProjectionName, seq, instanceID) - if err = row.Err(); err != nil { - return 0, errors.ThrowInternal(err, "CRDB-Unnex", "unable to update failure count") - } - if err = row.Scan(&count); err != nil { - return 0, errors.ThrowInternal(err, "CRDB-RwSMV", "unable to scan count") - } - return count, nil -} - -func (h *StatementHandler) setFailureCount(tx *sql.Tx, seq uint64, count uint, err error, instanceID string) error { - _, dbErr := tx.Exec(h.setFailureCountStmt, h.ProjectionName, seq, count, err.Error(), instanceID, "NOW()") - if dbErr != nil { - return errors.ThrowInternal(dbErr, "CRDB-4Ht4x", "set failure count failed") - } - return nil -} diff --git a/internal/eventstore/handler/crdb/handler_stmt.go b/internal/eventstore/handler/crdb/handler_stmt.go deleted file mode 100644 index 27418bf33..000000000 --- a/internal/eventstore/handler/crdb/handler_stmt.go +++ /dev/null @@ -1,347 +0,0 @@ -package crdb - -import ( - "context" - "database/sql" - "fmt" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/repository/pseudo" -) - -var ( - errSeqNotUpdated = errors.ThrowInternal(nil, "CRDB-79GWt", "current sequence not updated") -) - -type StatementHandlerConfig struct { - handler.ProjectionHandlerConfig - - Client *database.DB - SequenceTable string - LockTable string - FailedEventsTable string - MaxFailureCount uint - BulkLimit uint64 - - Reducers []handler.AggregateReducer - InitCheck *handler.Check -} - -type StatementHandler struct { - *handler.ProjectionHandler - Locker - - client *database.DB - sequenceTable string - currentSequenceStmt string - currentSequenceWithoutLockStmt string - updateSequencesBaseStmt string - maxFailureCount uint - failureCountStmt string - setFailureCountStmt string - - aggregates []eventstore.AggregateType - reduces map[eventstore.EventType]handler.Reduce - initCheck *handler.Check - initialized chan bool - - bulkLimit uint64 - - reduceScheduledPseudoEvent bool -} - -func NewStatementHandler( - ctx context.Context, - config StatementHandlerConfig, -) StatementHandler { - aggregateTypes := make([]eventstore.AggregateType, 0, len(config.Reducers)) - reduces := make(map[eventstore.EventType]handler.Reduce, len(config.Reducers)) - reduceScheduledPseudoEvent := false - for _, aggReducer := range config.Reducers { - aggregateTypes = append(aggregateTypes, aggReducer.Aggregate) - if aggReducer.Aggregate == pseudo.AggregateType { - reduceScheduledPseudoEvent = true - if len(config.Reducers) != 1 || - len(aggReducer.EventRedusers) != 1 || - aggReducer.EventRedusers[0].Event != pseudo.ScheduledEventType { - panic("if a pseudo.AggregateType is reduced, exactly one event reducer for pseudo.ScheduledEventType is supported and no other aggregate can be reduced") - } - } - for _, eventReducer := range aggReducer.EventRedusers { - reduces[eventReducer.Event] = eventReducer.Reduce - } - } - - h := StatementHandler{ - client: config.Client, - sequenceTable: config.SequenceTable, - maxFailureCount: config.MaxFailureCount, - currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, config.SequenceTable), - currentSequenceWithoutLockStmt: fmt.Sprintf(currentSequenceStmtWithoutLockFormat, config.SequenceTable), - updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, config.SequenceTable), - failureCountStmt: fmt.Sprintf(failureCountStmtFormat, config.FailedEventsTable), - setFailureCountStmt: fmt.Sprintf(setFailureCountStmtFormat, config.FailedEventsTable), - aggregates: aggregateTypes, - reduces: reduces, - bulkLimit: config.BulkLimit, - Locker: NewLocker(config.Client.DB, config.LockTable, config.ProjectionName), - initCheck: config.InitCheck, - initialized: make(chan bool), - reduceScheduledPseudoEvent: reduceScheduledPseudoEvent, - } - - h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.searchQuery, h.Lock, h.Unlock, h.initialized, reduceScheduledPseudoEvent) - - return h -} - -func (h *StatementHandler) Start() { - h.initialized <- true - close(h.initialized) - if !h.reduceScheduledPseudoEvent { - h.Subscribe(h.aggregates...) - } -} - -func (h *StatementHandler) searchQuery(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) { - if h.reduceScheduledPseudoEvent { - return nil, 1, nil - } - return h.dbSearchQuery(ctx, instanceIDs) -} - -func (h *StatementHandler) dbSearchQuery(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) { - sequences, err := h.currentSequences(ctx, false, h.client.QueryContext, instanceIDs) - if err != nil { - return nil, 0, err - } - - queryBuilder := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).Limit(h.bulkLimit).AllowTimeTravel() - - for _, aggregateType := range h.aggregates { - for _, instanceID := range instanceIDs { - var seq uint64 - for _, sequence := range sequences[aggregateType] { - if sequence.instanceID == instanceID { - seq = sequence.sequence - break - } - } - queryBuilder. - AddQuery(). - AggregateTypes(aggregateType). - SequenceGreater(seq). - InstanceID(instanceID) - } - } - return queryBuilder, h.bulkLimit, nil -} - -type transaction struct { - *sql.Tx -} - -func (t *transaction) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) error { - rows, err := t.Tx.QueryContext(ctx, query, args...) - if err != nil { - return err - } - defer func() { - closeErr := rows.Close() - logging.OnError(closeErr).Info("rows.Close failed") - }() - - if err = scan(rows); err != nil { - return err - } - return rows.Err() -} - -// Update implements handler.Update -func (h *StatementHandler) Update(ctx context.Context, stmts []*handler.Statement, reduce handler.Reduce) (index int, err error) { - if len(stmts) == 0 { - return -1, nil - } - instanceIDs := make([]string, 0, len(stmts)) - for _, stmt := range stmts { - instanceIDs = appendToInstanceIDs(instanceIDs, stmt.InstanceID) - } - tx, err := h.client.BeginTx(ctx, nil) - if err != nil { - return -1, errors.ThrowInternal(err, "CRDB-e89Gq", "begin failed") - } - - sequences, err := h.currentSequences(ctx, true, (&transaction{Tx: tx}).QueryContext, instanceIDs) - if err != nil { - tx.Rollback() - return -1, err - } - - //checks for events between create statement and current sequence - // because there could be events between current sequence and a creation event - // and we cannot check via stmt.PreviousSequence - if stmts[0].PreviousSequence == 0 { - previousStmts, err := h.fetchPreviousStmts(ctx, tx, stmts[0].Sequence, stmts[0].InstanceID, sequences, reduce) - if err != nil { - tx.Rollback() - return -1, err - } - stmts = append(previousStmts, stmts...) - } - - lastSuccessfulIdx := h.executeStmts(tx, &stmts, sequences) - - if lastSuccessfulIdx >= 0 { - err = h.updateCurrentSequences(tx, sequences) - if err != nil { - tx.Rollback() - return -1, err - } - } - - if err = tx.Commit(); err != nil { - return -1, err - } - - if lastSuccessfulIdx < len(stmts)-1 { - return lastSuccessfulIdx, handler.ErrSomeStmtsFailed - } - - return lastSuccessfulIdx, nil -} - -func (h *StatementHandler) fetchPreviousStmts(ctx context.Context, tx *sql.Tx, stmtSeq uint64, instanceID string, sequences currentSequences, reduce handler.Reduce) (previousStmts []*handler.Statement, err error) { - query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).SetTx(tx) - queriesAdded := false - for _, aggregateType := range h.aggregates { - for _, sequence := range sequences[aggregateType] { - if stmtSeq <= sequence.sequence && instanceID == sequence.instanceID { - continue - } - - query. - AddQuery(). - AggregateTypes(aggregateType). - SequenceGreater(sequence.sequence). - SequenceLess(stmtSeq). - InstanceID(sequence.instanceID) - - queriesAdded = true - } - } - - if !queriesAdded { - return nil, nil - } - - events, err := h.Eventstore.Filter(ctx, query) - if err != nil { - return nil, err - } - - for _, event := range events { - stmt, err := reduce(event) - if err != nil { - return nil, err - } - previousStmts = append(previousStmts, stmt) - } - return previousStmts, nil -} - -func (h *StatementHandler) executeStmts( - tx *sql.Tx, - stmts *[]*handler.Statement, - sequences currentSequences, -) int { - - lastSuccessfulIdx := -1 -stmts: - for i := 0; i < len(*stmts); i++ { - stmt := (*stmts)[i] - for _, sequence := range sequences[stmt.AggregateType] { - if stmt.Sequence <= sequence.sequence && stmt.InstanceID == sequence.instanceID { - logging.WithFields("statement", stmt, "currentSequence", sequence).Debug("statement dropped") - if i < len(*stmts)-1 { - copy((*stmts)[i:], (*stmts)[i+1:]) - } - *stmts = (*stmts)[:len(*stmts)-1] - i-- - continue stmts - } - if stmt.PreviousSequence > 0 && stmt.PreviousSequence != sequence.sequence && stmt.InstanceID == sequence.instanceID { - logging.WithFields("projection", h.ProjectionName, "aggregateType", stmt.AggregateType, "sequence", stmt.Sequence, "prevSeq", stmt.PreviousSequence, "currentSeq", sequence.sequence).Warn("sequences do not match") - break stmts - } - } - err := h.executeStmt(tx, stmt) - if err == nil { - updateSequences(sequences, stmt) - lastSuccessfulIdx = i - continue - } - - shouldContinue := h.handleFailedStmt(tx, stmt, err) - if !shouldContinue { - break - } - - updateSequences(sequences, stmt) - lastSuccessfulIdx = i - continue - } - return lastSuccessfulIdx -} - -// executeStmt handles sql statements -// an error is returned if the statement could not be inserted properly -func (h *StatementHandler) executeStmt(tx *sql.Tx, stmt *handler.Statement) error { - if stmt.IsNoop() { - return nil - } - _, err := tx.Exec("SAVEPOINT push_stmt") - if err != nil { - return errors.ThrowInternal(err, "CRDB-i1wp6", "unable to create savepoint") - } - err = stmt.Execute(tx, h.ProjectionName) - if err != nil { - logging.WithError(err).Error() - _, rollbackErr := tx.Exec("ROLLBACK TO SAVEPOINT push_stmt") - if rollbackErr != nil { - return errors.ThrowInternal(rollbackErr, "CRDB-zzp3P", "rollback to savepoint failed") - } - return errors.ThrowInternal(err, "CRDB-oRkaN", "unable execute stmt") - } - _, err = tx.Exec("RELEASE push_stmt") - if err != nil { - return errors.ThrowInternal(err, "CRDB-qWgwT", "unable to release savepoint") - } - return nil -} - -func updateSequences(sequences currentSequences, stmt *handler.Statement) { - for _, sequence := range sequences[stmt.AggregateType] { - if sequence.instanceID == stmt.InstanceID { - sequence.sequence = stmt.Sequence - return - } - } - sequences[stmt.AggregateType] = append(sequences[stmt.AggregateType], &instanceSequence{ - instanceID: stmt.InstanceID, - sequence: stmt.Sequence, - }) -} - -func appendToInstanceIDs(instances []string, id string) []string { - for _, instance := range instances { - if instance == id { - return instances - } - } - return append(instances, id) -} diff --git a/internal/eventstore/handler/crdb/handler_stmt_test.go b/internal/eventstore/handler/crdb/handler_stmt_test.go deleted file mode 100644 index 027469e71..000000000 --- a/internal/eventstore/handler/crdb/handler_stmt_test.go +++ /dev/null @@ -1,1817 +0,0 @@ -package crdb - -import ( - "context" - "database/sql" - "errors" - "fmt" - "reflect" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" - "github.com/stretchr/testify/assert" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" - es_repo_mock "github.com/zitadel/zitadel/internal/eventstore/repository/mock" - "github.com/zitadel/zitadel/internal/id" - "github.com/zitadel/zitadel/internal/repository/pseudo" -) - -var ( - errFilter = errors.New("filter err") - errReduce = errors.New("reduce err") -) - -var _ eventstore.Event = &testEvent{} - -type testEvent struct { - eventstore.BaseEvent - sequence uint64 - previousSequence uint64 - aggregateType eventstore.AggregateType - instanceID string -} - -func (e *testEvent) Sequence() uint64 { - return e.sequence -} - -func (e *testEvent) Aggregate() eventstore.Aggregate { - return eventstore.Aggregate{ - Type: e.aggregateType, - InstanceID: e.instanceID, - } -} - -func (e *testEvent) PreviousAggregateTypeSequence() uint64 { - return e.previousSequence -} - -func TestProjectionHandler_SearchQuery(t *testing.T) { - type want struct { - SearchQueryBuilder *eventstore.SearchQueryBuilder - limit uint64 - isErr func(error) bool - expectations []mockExpectation - } - type fields struct { - sequenceTable string - projectionName string - reducers []handler.AggregateReducer - bulkLimit uint64 - } - type args struct { - instanceIDs []string - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "error in current sequence", - fields: fields{ - sequenceTable: "my_sequences", - projectionName: "my_projection", - reducers: failingAggregateReducers("testAgg"), - bulkLimit: 5, - }, - args: args{ - instanceIDs: []string{"instanceID1"}, - }, - want: want{ - limit: 0, - isErr: func(err error) bool { - return errors.Is(err, sql.ErrTxDone) - }, - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequenceErr(false, "my_sequences", "my_projection", []string{"instanceID1"}, sql.ErrTxDone), - expectRollback(), - }, - SearchQueryBuilder: nil, - }, - }, - { - name: "only aggregates", - fields: fields{ - sequenceTable: "my_sequences", - projectionName: "my_projection", - reducers: failingAggregateReducers("testAgg"), - bulkLimit: 5, - }, - args: args{ - instanceIDs: []string{"instanceID1"}, - }, - want: want{ - limit: 5, - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1"}), - expectCommit(), - }, - SearchQueryBuilder: eventstore. - NewSearchQueryBuilder(eventstore.ColumnsEvent). - AllowTimeTravel(). - AddQuery(). - AggregateTypes("testAgg"). - SequenceGreater(5). - InstanceID("instanceID1"). - Builder(). - Limit(5), - }, - }, - { - name: "multiple instances", - fields: fields{ - sequenceTable: "my_sequences", - projectionName: "my_projection", - reducers: failingAggregateReducers("testAgg"), - bulkLimit: 5, - }, - args: args{ - instanceIDs: []string{"instanceID1", "instanceID2"}, - }, - want: want{ - limit: 5, - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID1", "instanceID2"}), - expectCommit(), - }, - SearchQueryBuilder: eventstore. - NewSearchQueryBuilder(eventstore.ColumnsEvent). - AllowTimeTravel(). - AddQuery(). - AggregateTypes("testAgg"). - SequenceGreater(5). - InstanceID("instanceID1"). - Or(). - AggregateTypes("testAgg"). - SequenceGreater(5). - InstanceID("instanceID2"). - Builder(). - Limit(5), - }, - }, - { - name: "scheduled pseudo event", - fields: fields{ - sequenceTable: "my_sequences", - projectionName: "my_projection", - reducers: []handler.AggregateReducer{{ - Aggregate: pseudo.AggregateType, - EventRedusers: []handler.EventReducer{ - { - Event: pseudo.ScheduledEventType, - Reduce: testReduceErr(errors.New("should not be called")), - }, - }, - }}, - bulkLimit: 5, - }, - args: args{ - instanceIDs: []string{"instanceID1", "instanceID2"}, - }, - want: want{ - limit: 1, - isErr: func(err error) bool { - return err == nil - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - id.Configure(&id.Config{Identification: id.Identification{PrivateIp: id.PrivateIp{Enabled: true}}}) - h := NewStatementHandler(context.Background(), StatementHandlerConfig{ - ProjectionHandlerConfig: handler.ProjectionHandlerConfig{ - ProjectionName: tt.fields.projectionName, - }, - SequenceTable: tt.fields.sequenceTable, - BulkLimit: tt.fields.bulkLimit, - Client: &database.DB{ - DB: client, - }, - Reducers: tt.fields.reducers, - }) - - for _, expectation := range tt.want.expectations { - expectation(mock) - } - - query, limit, err := h.searchQuery(context.Background(), tt.args.instanceIDs) - if !tt.want.isErr(err) { - t.Errorf("ProjectionHandler.prepareBulkStmts() error = %v", err) - return - } - - if !reflect.DeepEqual(query, tt.want.SearchQueryBuilder) { - t.Errorf("unexpected query: expected %v, got %v", tt.want.SearchQueryBuilder, query) - } - if tt.want.limit != limit { - t.Errorf("unexpected limit: got: %d want %d", limit, tt.want.limit) - } - - mock.MatchExpectationsInOrder(true) - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - }) - } -} - -func TestStatementHandler_Update(t *testing.T) { - type fields struct { - eventstore *eventstore.Eventstore - aggregates []eventstore.AggregateType - } - type want struct { - expectations []mockExpectation - isErr func(error) bool - stmtsLen int - } - type args struct { - ctx context.Context - stmts []*handler.Statement - reduce handler.Reduce - } - tests := []struct { - name string - fields fields - want want - args args - }{ - { - name: "begin fails", - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 0, - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBeginErr(sql.ErrConnDone), - }, - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - }, - }, - { - name: "current sequence fails", - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 0, - instanceID: "instanceID", - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequenceErr(false, "my_sequences", "my_projection", []string{"instanceID"}, sql.ErrTxDone), - expectRollback(), - }, - isErr: func(err error) bool { - return errors.Is(err, sql.ErrTxDone) - }, - }, - }, - { - name: "fetch previous fails", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t). - ExpectFilterEventsError(errFilter), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 0, - instanceID: "instanceID", - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), - expectRollback(), - }, - isErr: func(err error) bool { - return errors.Is(err, errFilter) - }, - stmtsLen: 1, - }, - }, - { - name: "no successful stmts", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "testAgg", - sequence: 7, - previousSequence: 6, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), - expectCommit(), - }, - isErr: func(err error) bool { - return errors.Is(err, handler.ErrSomeStmtsFailed) - }, - stmtsLen: 1, - }, - }, - { - name: "update current sequence fails", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t), - ), - ), - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 7, - previousSequence: 5, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectUpdateCurrentSequenceNoRows("my_sequences", "my_projection", 7, "agg", "instanceID"), - expectRollback(), - }, - isErr: func(err error) bool { - return errors.Is(err, errSeqNotUpdated) - }, - stmtsLen: 1, - }, - }, - { - name: "commit fails", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t), - ), - ), - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 7, - previousSequence: 5, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "agg", []string{"instanceID"}), - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "agg", "instanceID"), - expectCommitErr(sql.ErrConnDone), - }, - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - stmtsLen: 1, - }, - }, - { - name: "correct", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "testAgg", - sequence: 7, - previousSequence: 5, - instanceID: "instanceID", - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), - expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), - expectCommit(), - }, - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - stmtsLen: 0, - }, - }, - { - name: "fetch previous stmts no additional stmts", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "testAgg", - sequence: 7, - previousSequence: 0, - instanceID: "instanceID", - }), - }, - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), - expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), - expectCommit(), - }, - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - }, - }, - { - name: "fetch previous stmts additional events", - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - AggregateType: "testAgg", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "instanceID", - }, - ), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - args: args{ - ctx: context.Background(), - stmts: []*handler.Statement{ - NewNoOpStatement(&testEvent{ - aggregateType: "testAgg", - sequence: 7, - previousSequence: 0, - instanceID: "instanceID", - }), - }, - reduce: testReduce(), - }, - want: want{ - expectations: []mockExpectation{ - expectBegin(), - expectCurrentSequence(false, "my_sequences", "my_projection", 5, "testAgg", []string{"instanceID"}), - expectUpdateCurrentSequence("my_sequences", "my_projection", 7, "testAgg", "instanceID"), - expectCommit(), - }, - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - stmtsLen: 1, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - h := &StatementHandler{ - ProjectionHandler: &handler.ProjectionHandler{ - Handler: handler.Handler{ - Eventstore: tt.fields.eventstore, - }, - ProjectionName: "my_projection", - }, - sequenceTable: "my_sequences", - currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, "my_sequences"), - updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, "my_sequences"), - client: &database.DB{ - DB: client, - }, - } - - h.aggregates = tt.fields.aggregates - - for _, expectation := range tt.want.expectations { - expectation(mock) - } - - index, err := h.Update(tt.args.ctx, tt.args.stmts, tt.args.reduce) - if !tt.want.isErr(err) { - t.Errorf("StatementHandler.Update() error = %v", err) - } - if err == nil && tt.want.stmtsLen != index { - t.Errorf("wrong stmts length: want: %d got %d", tt.want.stmtsLen, index) - } - - mock.MatchExpectationsInOrder(true) - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - }) - } -} - -func TestProjectionHandler_fetchPreviousStmts(t *testing.T) { - type args struct { - ctx context.Context - stmtSeq uint64 - sequences currentSequences - reduce handler.Reduce - } - type want struct { - stmtCount int - isErr func(error) bool - } - type fields struct { - eventstore *eventstore.Eventstore - aggregates []eventstore.AggregateType - } - tests := []struct { - name string - args args - fields fields - want want - }{ - { - name: "no queries", - args: args{ - ctx: context.Background(), - reduce: testReduce(), - }, - fields: fields{ - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - stmtCount: 0, - }, - }, - { - name: "eventstore returns err", - args: args{ - ctx: context.Background(), - reduce: testReduce(), - sequences: currentSequences{ - "testAgg": []*instanceSequence{ - {sequence: 5}, - }, - }, - stmtSeq: 6, - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEventsError(errFilter), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, errFilter) - }, - stmtCount: 0, - }, - }, - { - name: "no events found", - args: args{ - ctx: context.Background(), - reduce: testReduce(), - sequences: currentSequences{ - "testAgg": []*instanceSequence{ - {sequence: 5}, - }, - }, - stmtSeq: 6, - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - name: "found events", - args: args{ - ctx: context.Background(), - reduce: testReduce(), - sequences: currentSequences{ - "testAgg": []*instanceSequence{ - {sequence: 5}, - }, - }, - stmtSeq: 10, - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 7, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - &repository.Event{ - ID: "id", - Sequence: 9, - PreviousAggregateSequence: 7, - CreationDate: time.Now(), - Type: "test.changed", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - want: want{ - stmtCount: 2, - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - name: "reduce fails", - args: args{ - ctx: context.Background(), - reduce: testReduceErr(errReduce), - sequences: currentSequences{ - "testAgg": []*instanceSequence{ - {sequence: 5}, - }, - }, - stmtSeq: 10, - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 7, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ), - aggregates: []eventstore.AggregateType{"testAgg"}, - }, - want: want{ - stmtCount: 0, - isErr: func(err error) bool { - return errors.Is(err, errReduce) - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &StatementHandler{ - aggregates: tt.fields.aggregates, - } - h.ProjectionHandler = &handler.ProjectionHandler{ - Handler: handler.Handler{ - Eventstore: tt.fields.eventstore, - }, - ProjectionName: "my_projection", - } - stmts, err := h.fetchPreviousStmts(tt.args.ctx, nil, tt.args.stmtSeq, "", tt.args.sequences, tt.args.reduce) - if !tt.want.isErr(err) { - t.Errorf("ProjectionHandler.prepareBulkStmts() error = %v", err) - return - } - if tt.want.stmtCount != len(stmts) { - t.Errorf("unexpected length of stmts: got: %d want %d", len(stmts), tt.want.stmtCount) - } - }) - } -} - -func TestStatementHandler_executeStmts(t *testing.T) { - type fields struct { - projectionName string - maxFailureCount uint - failedEventsTable string - } - type args struct { - stmts []*handler.Statement - sequences currentSequences - } - type want struct { - expectations []mockExpectation - idx int - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "already inserted", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 5, - previousSequence: 2, - }, - []handler.Column{ - { - Name: "col", - Value: "val1", - }, - }), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - {sequence: 5}, - }, - }, - }, - want: want{ - expectations: []mockExpectation{}, - idx: -1, - }, - }, - { - name: "previous sequence higher than sequence", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 5, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col1", - Value: "val1", - }, - }), - NewCreateStatement( - - &testEvent{ - aggregateType: "agg", - sequence: 8, - previousSequence: 7, - }, - []handler.Column{ - { - Name: "col2", - Value: "val2", - }, - }), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - {sequence: 2}, - }, - }, - }, - want: want{ - expectations: []mockExpectation{ - expectSavePoint(), - expectCreate("my_projection", []string{"col1"}, []string{"$1"}), - expectSavePointRelease(), - }, - idx: 0, - }, - }, - { - name: "execute fails not continue", - fields: fields{ - projectionName: "my_projection", - maxFailureCount: 5, - failedEventsTable: "failed_events", - }, - args: args{ - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 5, - previousSequence: 0, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 5, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 7, - previousSequence: 6, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - {sequence: 2}, - }, - }, - }, - want: want{ - expectations: []mockExpectation{ - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectSavePoint(), - expectCreateErr("my_projection", []string{"col"}, []string{"$1"}, sql.ErrConnDone), - expectSavePointRollback(), - expectFailureCount("failed_events", "my_projection", "instanceID", 6, 3), - expectUpdateFailureCount("failed_events", "my_projection", "instanceID", 6, 4), - }, - idx: 0, - }, - }, - { - name: "execute fails continue", - fields: fields{ - projectionName: "my_projection", - maxFailureCount: 5, - failedEventsTable: "failed_events", - }, - args: args{ - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 5, - previousSequence: 0, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col1", - Value: "val1", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 5, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col2", - Value: "val2", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 7, - previousSequence: 6, - instanceID: "instanceID", - }, - []handler.Column{ - { - Name: "col3", - Value: "val3", - }, - }), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - {sequence: 2}, - }, - }, - }, - want: want{ - expectations: []mockExpectation{ - expectSavePoint(), - expectCreate("my_projection", []string{"col1"}, []string{"$1"}), - expectSavePointRelease(), - expectSavePoint(), - expectCreateErr("my_projection", []string{"col2"}, []string{"$1"}, sql.ErrConnDone), - expectSavePointRollback(), - expectFailureCount("failed_events", "my_projection", "instanceID", 6, 4), - expectUpdateFailureCount("failed_events", "my_projection", "instanceID", 6, 5), - expectSavePoint(), - expectCreate("my_projection", []string{"col3"}, []string{"$1"}), - expectSavePointRelease(), - }, - idx: 2, - }, - }, - { - name: "correct", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmts: []*handler.Statement{ - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 5, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 6, - previousSequence: 5, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 7, - previousSequence: 6, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - NewMultiStatement( - &testEvent{ - aggregateType: "agg", - sequence: 8, - previousSequence: 7, - }, - AddCreateStatement( - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }, - ), - AddCreateStatement( - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }, - ), - ), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - {sequence: 2}, - }, - }, - }, - want: want{ - expectations: []mockExpectation{ - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - }, - idx: 3, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - h := NewStatementHandler( - context.Background(), - StatementHandlerConfig{ - ProjectionHandlerConfig: handler.ProjectionHandlerConfig{ - HandlerConfig: handler.HandlerConfig{ - Eventstore: nil, - }, - ProjectionName: tt.fields.projectionName, - RequeueEvery: 0, - }, - Client: &database.DB{ - DB: client, - }, - FailedEventsTable: tt.fields.failedEventsTable, - MaxFailureCount: tt.fields.maxFailureCount, - }, - ) - - mock.ExpectBegin() - - for _, expectation := range tt.want.expectations { - expectation(mock) - } - - mock.ExpectCommit() - - tx, err := client.Begin() - if err != nil { - t.Fatalf("unexpected err in begin: %v", err) - } - - idx := h.executeStmts(tx, &tt.args.stmts, tt.args.sequences) - if idx != tt.want.idx { - t.Errorf("unexpected index want: %d got %d", tt.want.idx, idx) - } - - if err := tx.Commit(); err != nil { - t.Fatalf("unexpected err in commit: %v", err) - } - - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - }) - } -} - -func TestStatementHandler_executeStmt(t *testing.T) { - type fields struct { - projectionName string - } - type args struct { - stmt *handler.Statement - } - type want struct { - expectations []mockExpectation - isErr func(error) bool - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "create savepoint fails", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmt: NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 1, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - expectations: []mockExpectation{ - expectSavePointErr(sql.ErrConnDone), - }, - }, - }, - { - name: "execute fails", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmt: NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 1, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrNoRows) - }, - expectations: []mockExpectation{ - expectSavePoint(), - expectCreateErr("my_projection", []string{"col"}, []string{"$1"}, sql.ErrNoRows), - expectSavePointRollback(), - }, - }, - }, - { - name: "rollback savepoint fails", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmt: NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 1, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - expectations: []mockExpectation{ - expectSavePoint(), - expectCreateErr("my_projection", []string{"col"}, []string{"$1"}, sql.ErrNoRows), - expectSavePointRollbackErr(sql.ErrConnDone), - }, - }, - }, - { - name: "no op", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmt: NewNoOpStatement(&testEvent{ - aggregateType: "agg", - sequence: 1, - previousSequence: 0, - }), - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{}, - }, - }, - { - name: "with op", - fields: fields{ - projectionName: "my_projection", - }, - args: args{ - stmt: NewCreateStatement( - &testEvent{ - aggregateType: "agg", - sequence: 1, - previousSequence: 0, - }, - []handler.Column{ - { - Name: "col", - Value: "val", - }, - }), - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{ - expectSavePoint(), - expectCreate("my_projection", []string{"col"}, []string{"$1"}), - expectSavePointRelease(), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &StatementHandler{ - ProjectionHandler: &handler.ProjectionHandler{ - ProjectionName: tt.fields.projectionName, - }, - } - - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - mock.ExpectBegin() - - for _, expectation := range tt.want.expectations { - expectation(mock) - } - - mock.ExpectCommit() - - tx, err := client.Begin() - if err != nil { - t.Fatalf("unexpected err in begin: %v", err) - } - - err = h.executeStmt(tx, tt.args.stmt) - if !tt.want.isErr(err) { - t.Errorf("unexpected error: %v", err) - } - - if err = tx.Commit(); err != nil { - t.Fatalf("unexpected err in begin: %v", err) - } - - mock.MatchExpectationsInOrder(true) - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - }) - } -} - -func TestStatementHandler_currentSequence(t *testing.T) { - type fields struct { - sequenceTable string - projectionName string - aggregates []eventstore.AggregateType - } - type args struct { - stmt handler.Statement - instanceIDs []string - } - type want struct { - expectations []mockExpectation - isErr func(error) bool - sequences currentSequences - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "error in query", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - }, - args: args{ - stmt: handler.Statement{}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - expectations: []mockExpectation{ - expectCurrentSequenceErr(true, "my_table", "my_projection", nil, sql.ErrConnDone), - }, - }, - }, - { - name: "no rows", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - stmt: handler.Statement{}, - instanceIDs: []string{"instanceID"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - expectations: []mockExpectation{ - expectCurrentSequenceNoRows("my_table", "my_projection", []string{"instanceID"}), - }, - sequences: currentSequences{}, - }, - }, - { - name: "scan err", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - stmt: handler.Statement{}, - instanceIDs: []string{"instanceID"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrTxDone) - }, - expectations: []mockExpectation{ - expectCurrentSequenceScanErr("my_table", "my_projection", []string{"instanceID"}), - }, - sequences: currentSequences{}, - }, - }, - { - name: "found", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - stmt: handler.Statement{}, - instanceIDs: []string{"instanceID"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - expectations: []mockExpectation{ - expectCurrentSequence(true, "my_table", "my_projection", 5, "agg", []string{"instanceID"}), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - }, - }, - }, - { - name: "multiple found", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - stmt: handler.Statement{}, - instanceIDs: []string{"instanceID1", "instanceID2"}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, nil) - }, - expectations: []mockExpectation{ - expectCurrentSequence(true, "my_table", "my_projection", 5, "agg", []string{"instanceID1", "instanceID2"}), - }, - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID1", - }, - { - sequence: 5, - instanceID: "instanceID2", - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &StatementHandler{ - ProjectionHandler: &handler.ProjectionHandler{ - ProjectionName: tt.fields.projectionName, - }, - sequenceTable: tt.fields.sequenceTable, - currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, tt.fields.sequenceTable), - } - - h.aggregates = tt.fields.aggregates - - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - mock.ExpectBegin() - - for _, expectation := range tt.want.expectations { - expectation(mock) - } - - mock.ExpectCommit() - - tx, err := client.Begin() - if err != nil { - t.Fatalf("unexpected err in begin: %v", err) - } - - seq, err := h.currentSequences(context.Background(), true, (&transaction{Tx: tx}).QueryContext, tt.args.instanceIDs) - if !tt.want.isErr(err) { - t.Errorf("unexpected error: %v", err) - } - - if err = tx.Commit(); err != nil { - t.Fatalf("unexpected err in commit: %v", err) - } - - mock.MatchExpectationsInOrder(true) - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - - for _, aggregateType := range tt.fields.aggregates { - assert.Equal(t, tt.want.sequences[aggregateType], seq[aggregateType]) - } - }) - } -} - -func TestStatementHandler_updateCurrentSequence(t *testing.T) { - type fields struct { - sequenceTable string - projectionName string - aggregates []eventstore.AggregateType - } - type args struct { - sequences currentSequences - } - type want struct { - expectations []mockExpectation - isErr func(error) bool - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "update sequence fails", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - }, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, sql.ErrConnDone) - }, - expectations: []mockExpectation{ - expectUpdateCurrentSequenceErr("my_table", "my_projection", 5, sql.ErrConnDone, "agg", "instanceID"), - }, - }, - }, - { - name: "update sequence returns no rows", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - }, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, errSeqNotUpdated) - }, - expectations: []mockExpectation{ - expectUpdateCurrentSequenceNoRows("my_table", "my_projection", 5, "agg", "instanceID"), - }, - }, - }, - { - name: "correct", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - }, - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{ - expectUpdateCurrentSequence("my_table", "my_projection", 5, "agg", "instanceID"), - }, - }, - }, - { - name: "multiple sequences", - fields: fields{ - sequenceTable: "my_table", - projectionName: "my_projection", - aggregates: []eventstore.AggregateType{"agg"}, - }, - args: args{ - sequences: currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - "agg2": []*instanceSequence{ - { - sequence: 6, - instanceID: "instanceID", - }, - { - sequence: 10, - instanceID: "instanceID2", - }, - }, - }, - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - expectations: []mockExpectation{ - expectUpdateThreeCurrentSequence(t, "my_table", "my_projection", currentSequences{ - "agg": []*instanceSequence{ - { - sequence: 5, - instanceID: "instanceID", - }, - }, - "agg2": []*instanceSequence{ - { - sequence: 6, - instanceID: "instanceID", - }, - { - sequence: 10, - instanceID: "instanceID2", - }, - }, - }), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - h := &StatementHandler{ - ProjectionHandler: &handler.ProjectionHandler{ - ProjectionName: tt.fields.projectionName, - }, - sequenceTable: tt.fields.sequenceTable, - updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, tt.fields.sequenceTable), - } - - h.aggregates = tt.fields.aggregates - - client, mock, err := sqlmock.New() - if err != nil { - t.Fatal(err) - } - defer client.Close() - - mock.ExpectBegin() - for _, expectation := range tt.want.expectations { - expectation(mock) - } - mock.ExpectCommit() - - tx, err := client.Begin() - if err != nil { - t.Fatalf("unexpected error in begin: %v", err) - } - - err = h.updateCurrentSequences(tx, tt.args.sequences) - if !tt.want.isErr(err) { - t.Errorf("unexpected error: %v", err) - } - - err = tx.Commit() - if err != nil { - t.Fatalf("unexpected error in commit: %v", err) - } - - mock.MatchExpectationsInOrder(true) - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("expectations not met: %v", err) - } - }) - } -} - -func testReduce() handler.Reduce { - return func(event eventstore.Event) (*handler.Statement, error) { - return NewNoOpStatement(event), nil - } -} - -func testReduceErr(err error) handler.Reduce { - return func(event eventstore.Event) (*handler.Statement, error) { - return nil, err - } -} - -func failingAggregateReducers(aggregates ...eventstore.AggregateType) []handler.AggregateReducer { - reducers := make([]handler.AggregateReducer, len(aggregates)) - for idx := range aggregates { - reducers[idx] = handler.AggregateReducer{ - Aggregate: aggregates[idx], - EventRedusers: []handler.EventReducer{{ - Event: "any.event", - Reduce: testReduceErr(errors.New("should not be called")), - }}, - } - } - return reducers -} diff --git a/internal/eventstore/handler/crdb/init_test.go b/internal/eventstore/handler/crdb/init_test.go deleted file mode 100644 index 1e7e6bd82..000000000 --- a/internal/eventstore/handler/crdb/init_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package crdb - -import "testing" - -func Test_defaultValue(t *testing.T) { - type args struct { - value interface{} - } - tests := []struct { - name string - args args - want string - }{ - { - name: "string", - args: args{ - value: "asdf", - }, - want: "'asdf'", - }, - { - name: "primitive non string", - args: args{ - value: 1, - }, - want: "1", - }, - { - name: "stringer", - args: args{ - value: testStringer(0), - }, - want: "0", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := defaultValue(tt.args.value); got != tt.want { - t.Errorf("defaultValue() = %v, want %v", got, tt.want) - } - }) - } -} - -type testStringer int - -func (t testStringer) String() string { - return "0529958243" -} diff --git a/internal/eventstore/handler/crdb/lock.go b/internal/eventstore/handler/crdb/lock.go index d101a3409..e751eff2a 100644 --- a/internal/eventstore/handler/crdb/lock.go +++ b/internal/eventstore/handler/crdb/lock.go @@ -91,7 +91,7 @@ func (h *locker) Unlock(instanceIDs ...string) error { return nil } -func (h *locker) lockStatement(lockDuration time.Duration, instanceIDs database.StringArray) (string, []interface{}) { +func (h *locker) lockStatement(lockDuration time.Duration, instanceIDs database.TextArray[string]) (string, []interface{}) { valueQueries := make([]string, len(instanceIDs)) values := make([]interface{}, len(instanceIDs)+4) values[0] = h.workerName diff --git a/internal/eventstore/handler/crdb/lock_test.go b/internal/eventstore/handler/crdb/lock_test.go index fe18253b5..5ab824ee6 100644 --- a/internal/eventstore/handler/crdb/lock_test.go +++ b/internal/eventstore/handler/crdb/lock_test.go @@ -158,7 +158,7 @@ func TestStatementHandler_renewLock(t *testing.T) { }, args: args{ lockDuration: 1 * time.Second, - instanceIDs: database.StringArray{"instanceID"}, + instanceIDs: database.TextArray[string]{"instanceID"}, }, }, { @@ -173,7 +173,7 @@ func TestStatementHandler_renewLock(t *testing.T) { }, args: args{ lockDuration: 2 * time.Second, - instanceIDs: database.StringArray{"instanceID"}, + instanceIDs: database.TextArray[string]{"instanceID"}, }, }, { @@ -188,7 +188,7 @@ func TestStatementHandler_renewLock(t *testing.T) { }, args: args{ lockDuration: 3 * time.Second, - instanceIDs: database.StringArray{"instanceID"}, + instanceIDs: database.TextArray[string]{"instanceID"}, }, }, { diff --git a/internal/eventstore/handler/crdb/reduce.go b/internal/eventstore/handler/crdb/reduce.go deleted file mode 100644 index bda49c5a1..000000000 --- a/internal/eventstore/handler/crdb/reduce.go +++ /dev/null @@ -1,16 +0,0 @@ -package crdb - -import ( - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" -) - -//reduce implements handler.Reduce function -func (h *StatementHandler) reduce(event eventstore.Event) (*handler.Statement, error) { - reduce, ok := h.reduces[event.Type()] - if !ok { - return NewNoOpStatement(event), nil - } - - return reduce(event) -} diff --git a/internal/eventstore/handler/handler.go b/internal/eventstore/handler/handler.go deleted file mode 100644 index 1944cfd0b..000000000 --- a/internal/eventstore/handler/handler.go +++ /dev/null @@ -1,36 +0,0 @@ -package handler - -import ( - "github.com/zitadel/zitadel/internal/eventstore" -) - -type HandlerConfig struct { - Eventstore *eventstore.Eventstore -} -type Handler struct { - Eventstore *eventstore.Eventstore - Sub *eventstore.Subscription - EventQueue chan eventstore.Event -} - -func NewHandler(config HandlerConfig) Handler { - return Handler{ - Eventstore: config.Eventstore, - EventQueue: make(chan eventstore.Event, 100), - } -} - -func (h *Handler) Subscribe(aggregates ...eventstore.AggregateType) { - h.Sub = eventstore.SubscribeAggregates(h.EventQueue, aggregates...) -} - -func (h *Handler) SubscribeEvents(types map[eventstore.AggregateType][]eventstore.EventType) { - h.Sub = eventstore.SubscribeEventTypes(h.EventQueue, types) -} - -func (h *Handler) Unsubscribe() { - if h.Sub == nil { - return - } - h.Sub.Unsubscribe() -} diff --git a/internal/eventstore/handler/handler_projection.go b/internal/eventstore/handler/handler_projection.go deleted file mode 100644 index 2dbcfc9aa..000000000 --- a/internal/eventstore/handler/handler_projection.go +++ /dev/null @@ -1,396 +0,0 @@ -package handler - -import ( - "context" - "errors" - "runtime/debug" - "time" - - "github.com/sirupsen/logrus" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/api/call" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/pseudo" -) - -const ( - schedulerSucceeded = eventstore.EventType("system.projections.scheduler.succeeded") - aggregateType = eventstore.AggregateType("system") - aggregateID = "SYSTEM" -) - -type ProjectionHandlerConfig struct { - HandlerConfig - ProjectionName string - RequeueEvery time.Duration - RetryFailedAfter time.Duration - Retries uint - ConcurrentInstances uint - HandleActiveInstances time.Duration -} - -// Update updates the projection with the given statements -type Update func(context.Context, []*Statement, Reduce) (index int, err error) - -// Reduce reduces the given event to a statement -// which is used to update the projection -type Reduce func(eventstore.Event) (*Statement, error) - -// SearchQuery generates the search query to lookup for events -type SearchQuery func(ctx context.Context, instanceIDs []string) (query *eventstore.SearchQueryBuilder, queryLimit uint64, err error) - -// Lock is used for mutex handling if needed on the projection -type Lock func(context.Context, time.Duration, ...string) <-chan error - -// Unlock releases the mutex of the projection -type Unlock func(...string) error - -// NowFunc makes time.Now() mockable -type NowFunc func() time.Time - -type ProjectionHandler struct { - Handler - ProjectionName string - reduce Reduce - update Update - searchQuery SearchQuery - triggerProjection *time.Timer - lock Lock - unlock Unlock - requeueAfter time.Duration - retryFailedAfter time.Duration - retries int - concurrentInstances int - handleActiveInstances time.Duration - nowFunc NowFunc - reduceScheduledPseudoEvent bool -} - -func NewProjectionHandler( - ctx context.Context, - config ProjectionHandlerConfig, - reduce Reduce, - update Update, - query SearchQuery, - lock Lock, - unlock Unlock, - initialized <-chan bool, - reduceScheduledPseudoEvent bool, -) *ProjectionHandler { - concurrentInstances := int(config.ConcurrentInstances) - if concurrentInstances < 1 { - concurrentInstances = 1 - } - h := &ProjectionHandler{ - Handler: NewHandler(config.HandlerConfig), - ProjectionName: config.ProjectionName, - reduce: reduce, - update: update, - searchQuery: query, - lock: lock, - unlock: unlock, - requeueAfter: config.RequeueEvery, - triggerProjection: time.NewTimer(0), // first trigger is instant on startup - retryFailedAfter: config.RetryFailedAfter, - retries: int(config.Retries), - concurrentInstances: concurrentInstances, - handleActiveInstances: config.HandleActiveInstances, - nowFunc: time.Now, - reduceScheduledPseudoEvent: reduceScheduledPseudoEvent, - } - - go func() { - <-initialized - if !h.reduceScheduledPseudoEvent { - go h.subscribe(ctx) - } - go h.schedule(ctx) - }() - - return h -} - -func triggerInstances(ctx context.Context, instances []string) []string { - if len(instances) == 0 { - instances = append(instances, authz.GetInstance(ctx).InstanceID()) - } - return instances -} - -// Trigger handles all events for the provided instances (or current instance from context if non specified) -// by calling FetchEvents and Process until the amount of events is smaller than the BulkLimit. -// If a bulk action was executed, the call timestamp in context will be reset for subsequent queries. -// The returned context is never nil. It is either the original context or an updated context. -// -// If Trigger encounters an error, it is only logged. If the error is important for the caller, -// use TriggerErr instead. -func (h *ProjectionHandler) Trigger(ctx context.Context, instances ...string) context.Context { - instances = triggerInstances(ctx, instances) - ctx, err := h.TriggerErr(ctx, instances...) - logging.OnError(err).WithFields(logrus.Fields{ - "projection": h.ProjectionName, - "instanceIDs": instances, - }).Error("trigger failed") - return ctx -} - -// TriggerErr handles all events for the provided instances (or current instance from context if non specified) -// by calling FetchEvents and Process until the amount of events is smaller than the BulkLimit. -// If a bulk action was executed, the call timestamp in context will be reset for subsequent queries. -// The returned context is never nil. It is either the original context or an updated context. -func (h *ProjectionHandler) TriggerErr(ctx context.Context, instances ...string) (outCtx context.Context, err error) { - instances = triggerInstances(ctx, instances) - defer func() { - outCtx = call.ResetTimestamp(ctx) - }() - for { - events, hasLimitExceeded, err := h.FetchEvents(ctx, instances...) - if err != nil { - return ctx, err - } - if len(events) == 0 { - return ctx, nil - } - _, err = h.Process(ctx, events...) - if err != nil { - return ctx, err - } - if !hasLimitExceeded { - return ctx, nil - } - } -} - -// Process handles multiple events by reducing them to statements and updating the projection -func (h *ProjectionHandler) Process(ctx context.Context, events ...eventstore.Event) (index int, err error) { - if len(events) == 0 { - return 0, nil - } - index = -1 - statements := make([]*Statement, len(events)) - for i, event := range events { - statements[i], err = h.reduce(event) - if err != nil { - return index, err - } - } - for retry := 0; retry <= h.retries; retry++ { - index, err = h.update(ctx, statements[index+1:], h.reduce) - if err != nil && !errors.Is(err, ErrSomeStmtsFailed) { - return index, err - } - if err == nil { - return index, nil - } - time.Sleep(h.retryFailedAfter) - } - return index, err -} - -// FetchEvents checks the current sequences and filters for newer events -func (h *ProjectionHandler) FetchEvents(ctx context.Context, instances ...string) ([]eventstore.Event, bool, error) { - if h.reduceScheduledPseudoEvent { - return h.fetchPseudoEvents(ctx, instances...) - } - return h.fetchDBEvents(ctx, instances...) -} - -func (h *ProjectionHandler) fetchDBEvents(ctx context.Context, instances ...string) ([]eventstore.Event, bool, error) { - eventQuery, eventsLimit, err := h.searchQuery(ctx, instances) - if err != nil { - return nil, false, err - } - events, err := h.Eventstore.Filter(ctx, eventQuery) - if err != nil { - return nil, false, err - } - return events, int(eventsLimit) == len(events), err -} - -func (h *ProjectionHandler) fetchPseudoEvents(ctx context.Context, instances ...string) ([]eventstore.Event, bool, error) { - return []eventstore.Event{pseudo.NewScheduledEvent(ctx, time.Now(), instances...)}, false, nil -} - -func (h *ProjectionHandler) subscribe(ctx context.Context) { - ctx, cancel := context.WithCancel(ctx) - defer func() { - err := recover() - if err != nil { - h.Handler.Unsubscribe() - logging.WithFields("projection", h.ProjectionName).Errorf("subscription panicked: %v", err) - } - cancel() - }() - for firstEvent := range h.EventQueue { - events := checkAdditionalEvents(h.EventQueue, firstEvent) - - index, err := h.Process(ctx, events...) - if err != nil || index < len(events)-1 { - logging.WithFields("projection", h.ProjectionName).WithError(err).Warn("unable to process all events from subscription") - } - } -} - -func (h *ProjectionHandler) schedule(ctx context.Context) { - ctx, cancel := context.WithCancel(ctx) - defer func() { - err := recover() - if err != nil { - logging.WithFields("projection", h.ProjectionName, "cause", err, "stack", string(debug.Stack())).Error("schedule panicked") - } - cancel() - }() - // flag if projection has been successfully executed at least once since start - var succeededOnce bool - var err error - // get every instance id except empty (system) - query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).AllowTimeTravel().AddQuery().ExcludedInstanceID("") - for range h.triggerProjection.C { - if !succeededOnce { - // (re)check if it has succeeded in the meantime - succeededOnce, err = h.hasSucceededOnce(ctx) - if err != nil { - logging.WithFields("projection", h.ProjectionName, "err", err). - Error("schedule could not check if projection has already succeeded once") - h.triggerProjection.Reset(h.requeueAfter) - continue - } - } - lockCtx := ctx - var cancelLock context.CancelFunc - // if it still has not succeeded, lock the projection for the system - // so that only a single scheduler does a first schedule (of every instance) - if !succeededOnce { - lockCtx, cancelLock = context.WithCancel(ctx) - errs := h.lock(lockCtx, h.requeueAfter, "system") - if err, ok := <-errs; err != nil || !ok { - cancelLock() - logging.WithFields("projection", h.ProjectionName).OnError(err).Debug("initial lock failed for first schedule") - h.triggerProjection.Reset(h.requeueAfter) - continue - } - go h.cancelOnErr(lockCtx, errs, cancelLock) - } - if succeededOnce { - // since we have at least one successful run, we can restrict it to events not older than - // h.handleActiveInstances (just to be sure not to miss an event) - // This ensures that only instances with recent events on the handler are projected - query = query.CreationDateAfter(h.nowFunc().Add(-1 * h.handleActiveInstances)) - } - ids, err := h.Eventstore.InstanceIDs(ctx, h.requeueAfter, !succeededOnce, query.Builder()) - if err != nil { - logging.WithFields("projection", h.ProjectionName).WithError(err).Error("instance ids") - h.triggerProjection.Reset(h.requeueAfter) - continue - } - var failed bool - for i := 0; i < len(ids); i = i + h.concurrentInstances { - max := i + h.concurrentInstances - if max > len(ids) { - max = len(ids) - } - instances := ids[i:max] - lockInstanceCtx, cancelInstanceLock := context.WithCancel(lockCtx) - errs := h.lock(lockInstanceCtx, h.requeueAfter, instances...) - //wait until projection is locked - if err, ok := <-errs; err != nil || !ok { - cancelInstanceLock() - logging.WithFields("projection", h.ProjectionName).OnError(err).Debug("initial lock failed") - failed = true - continue - } - go h.cancelOnErr(lockInstanceCtx, errs, cancelInstanceLock) - _, err = h.TriggerErr(lockInstanceCtx, instances...) - if err != nil { - logging.WithFields("projection", h.ProjectionName, "instanceIDs", instances).WithError(err).Error("trigger failed") - failed = true - } - - cancelInstanceLock() - unlockErr := h.unlock(instances...) - logging.WithFields("projection", h.ProjectionName).OnError(unlockErr).Warn("unable to unlock") - } - // if the first schedule did not fail, store that in the eventstore, so we can check on later starts - if !succeededOnce { - if !failed { - err = h.setSucceededOnce(ctx) - logging.WithFields("projection", h.ProjectionName).OnError(err).Warn("unable to push first schedule succeeded") - } - cancelLock() - unlockErr := h.unlock("system") - logging.WithFields("projection", h.ProjectionName).OnError(unlockErr).Warn("unable to unlock first schedule") - } - // it succeeded at least once if it has succeeded before or if it has succeeded now - not failed ;-) - succeededOnce = succeededOnce || !failed - h.triggerProjection.Reset(h.requeueAfter) - } -} - -func (h *ProjectionHandler) hasSucceededOnce(ctx context.Context) (bool, error) { - events, err := h.Eventstore.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes(aggregateType). - AggregateIDs(aggregateID). - EventTypes(schedulerSucceeded). - EventData(map[string]interface{}{ - "name": h.ProjectionName, - }). - Builder(), - ) - return len(events) > 0 && err == nil, err -} - -func (h *ProjectionHandler) setSucceededOnce(ctx context.Context) error { - _, err := h.Eventstore.Push(ctx, &ProjectionSucceededEvent{ - BaseEvent: *eventstore.NewBaseEventForPush(ctx, - eventstore.NewAggregate(ctx, aggregateID, aggregateType, "v1"), - schedulerSucceeded, - ), - Name: h.ProjectionName, - }) - return err -} - -type ProjectionSucceededEvent struct { - eventstore.BaseEvent `json:"-"` - Name string `json:"name"` -} - -func (p *ProjectionSucceededEvent) Data() interface{} { - return p -} - -func (p *ProjectionSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return nil -} - -func (h *ProjectionHandler) cancelOnErr(ctx context.Context, errs <-chan error, cancel func()) { - for { - select { - case err := <-errs: - if err != nil { - logging.WithFields("projection", h.ProjectionName).WithError(err).Warn("bulk canceled") - cancel() - return - } - case <-ctx.Done(): - cancel() - return - } - } -} - -func checkAdditionalEvents(eventQueue chan eventstore.Event, event eventstore.Event) []eventstore.Event { - events := make([]eventstore.Event, 1) - events[0] = event - for { - select { - case event := <-eventQueue: - events = append(events, event) - default: - return events - } - } -} diff --git a/internal/eventstore/handler/handler_projection_test.go b/internal/eventstore/handler/handler_projection_test.go deleted file mode 100644 index 3ee791a2d..000000000 --- a/internal/eventstore/handler/handler_projection_test.go +++ /dev/null @@ -1,1228 +0,0 @@ -package handler - -import ( - "context" - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/api/call" - "github.com/zitadel/zitadel/internal/api/service" - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" - es_repo_mock "github.com/zitadel/zitadel/internal/eventstore/repository/mock" -) - -var ( - ErrQuery = errors.New("query err") - ErrFilter = errors.New("filter err") - ErrReduce = errors.New("reduce err") - ErrLock = errors.New("lock failed") - ErrUnlock = errors.New("unlock failed") - ErrExec = errors.New("exec error") - ErrBulk = errors.New("bulk err") - ErrUpdate = errors.New("update err") -) - -func TestProjectionHandler_Trigger(t *testing.T) { - const pause = time.Millisecond - - startCtx := call.WithTimestamp(context.Background()) - start := call.FromContext(startCtx) - - h := &ProjectionHandler{ - Handler: Handler{ - Eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - )), - }, - ProjectionName: "test", - reduce: testReduce(newTestStatement("testAgg", 1, 0)), - update: testUpdate(t, 1, 0, nil), - searchQuery: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, nil, - ), - } - - time.Sleep(pause) - endCtx := h.Trigger(startCtx) - // check if the new context has a call timestamp that's later than start+pause. - assert.WithinRange(t, call.FromContext(endCtx), start.Add(pause), start.Add(pause+time.Second)) -} - -func TestProjectionHandler_TriggerErr(t *testing.T) { - type fields struct { - reduce Reduce - update Update - query SearchQuery - eventstore func(t *testing.T) *eventstore.Eventstore - } - type args struct { - ctx context.Context - instances []string - } - type want struct { - isErr func(err error) bool - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - "query error", - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return nil - }, - query: testQuery(nil, 0, ErrQuery), - }, - args{ - context.Background(), - nil, - }, - want{isErr: func(err error) bool { - return errors.Is(err, ErrQuery) - }}, - }, - { - "no events", - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore( - eventstore.TestConfig(es_repo_mock.NewRepo(t).ExpectFilterEvents()), - ) - }, - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil), - }, - args{ - context.Background(), - nil, - }, - want{ - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - "process error", - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ) - }, - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil), - reduce: testReduceErr(ErrReduce), - }, - args{ - context.Background(), - nil, - }, - want{ - isErr: func(err error) bool { - return errors.Is(err, ErrReduce) - }, - }, - }, - { - "process ok", - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ) - }, - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil), - reduce: testReduce(newTestStatement("testAgg", 1, 0)), - update: testUpdate(t, 1, 0, nil), - }, - args{ - context.Background(), - nil, - }, - want{ - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - "process limit exceeded ok", - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t). - ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ).ExpectFilterEvents(), - ), - ) - }, - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 1, - nil), - reduce: testReduce(newTestStatement("testAgg", 1, 0)), - update: testUpdate(t, 1, 0, nil), - }, - args{ - context.Background(), - nil, - }, - want{ - isErr: func(err error) bool { - return err == nil - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &ProjectionHandler{ - Handler: Handler{ - Eventstore: tt.fields.eventstore(t), - }, - ProjectionName: "test", - reduce: tt.fields.reduce, - update: tt.fields.update, - searchQuery: tt.fields.query, - } - - // context timestamp is checked in [TestProjectionHandler_Trigger] - _, err := h.TriggerErr(tt.args.ctx, tt.args.instances...) - if !tt.want.isErr(err) { - t.Errorf("unexpected error %v", err) - } - }) - } -} - -func TestProjectionHandler_Process(t *testing.T) { - type fields struct { - reduce Reduce - update Update - } - type args struct { - ctx context.Context - events []eventstore.Event - } - type want struct { - isErr func(err error) bool - index int - } - tests := []struct { - name string - fields fields - args args - want want - }{ - { - name: "no events", - fields: fields{}, - args: args{}, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - index: 0, - }, - }, - { - name: "reduce fails", - fields: fields{ - reduce: testReduceErr(ErrReduce), - }, - args: args{ - events: []eventstore.Event{newTestEvent("id", "description", nil)}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, ErrReduce) - }, - index: -1, - }, - }, - { - name: "stmt failed", - fields: fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, -1, ErrSomeStmtsFailed), - }, - args: args{ - events: []eventstore.Event{newTestEvent("id", "description", nil)}, - }, - want: want{ - isErr: func(err error) bool { - return errors.Is(err, ErrSomeStmtsFailed) - }, - index: -1, - }, - }, - { - name: "stmt error", - fields: fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, -1, errors.New("some error")), - }, - args: args{ - events: []eventstore.Event{newTestEvent("id", "description", nil)}, - }, - want: want{ - isErr: func(err error) bool { - return err.Error() == "some error" - }, - index: -1, - }, - }, - { - name: "stmt succeeded", - fields: fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, 0, nil), - }, - args: args{ - events: []eventstore.Event{newTestEvent("id", "description", nil)}, - }, - want: want{ - isErr: func(err error) bool { - return err == nil - }, - index: 0, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := NewProjectionHandler( - context.Background(), - ProjectionHandlerConfig{ - HandlerConfig: HandlerConfig{ - Eventstore: nil, - }, - ProjectionName: "test", - RequeueEvery: -1, - }, - tt.fields.reduce, - tt.fields.update, - nil, - nil, - nil, - nil, - false, - ) - - index, err := h.Process(tt.args.ctx, tt.args.events...) - if !tt.want.isErr(err) { - t.Errorf("unexpected error %v", err) - } - assert.Equal(t, tt.want.index, index) - }) - } -} - -func TestProjectionHandler_FetchEvents(t *testing.T) { - type args struct { - ctx context.Context - instanceIDs []string - } - type want struct { - limitExceeded bool - isErr func(error) bool - } - type fields struct { - eventstore *eventstore.Eventstore - query SearchQuery - } - tests := []struct { - name string - args args - fields fields - want want - }{ - { - name: "query returns err", - args: args{ - ctx: context.Background(), - }, - fields: fields{ - query: testQuery(nil, 0, ErrQuery), - }, - want: want{ - limitExceeded: false, - isErr: func(err error) bool { - return errors.Is(err, ErrQuery) - }, - }, - }, - { - name: "eventstore returns err", - args: args{ - ctx: context.Background(), - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEventsError(ErrFilter), - ), - ), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil, - ), - }, - want: want{ - limitExceeded: false, - isErr: func(err error) bool { - return errors.Is(err, ErrFilter) - }, - }, - }, - { - name: "no events found", - args: args{ - ctx: context.Background(), - }, - fields: fields{ - eventstore: eventstore.NewEventstore( - eventstore.TestConfig(es_repo_mock.NewRepo(t).ExpectFilterEvents()), - ), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil, - ), - }, - want: want{ - limitExceeded: false, - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - name: "found events smaller than limit", - args: args{ - ctx: context.Background(), - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - &repository.Event{ - ID: "id", - Sequence: 2, - PreviousAggregateSequence: 1, - CreationDate: time.Now(), - Type: "test.changed", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 5, - nil, - ), - }, - want: want{ - limitExceeded: false, - isErr: func(err error) bool { - return err == nil - }, - }, - }, - { - name: "found events exceeds limit", - args: args{ - ctx: context.Background(), - }, - fields: fields{ - eventstore: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - ID: "id", - Sequence: 1, - PreviousAggregateSequence: 0, - CreationDate: time.Now(), - Type: "test.added", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - &repository.Event{ - ID: "id", - Sequence: 2, - PreviousAggregateSequence: 1, - CreationDate: time.Now(), - Type: "test.changed", - Version: "v1", - AggregateID: "testid", - AggregateType: "testAgg", - }, - ), - ), - ), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 2, - nil, - ), - }, - want: want{ - limitExceeded: true, - isErr: func(err error) bool { - return err == nil - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &ProjectionHandler{ - Handler: Handler{ - Eventstore: tt.fields.eventstore, - }, - searchQuery: tt.fields.query, - } - _, limitExceeded, err := h.FetchEvents(tt.args.ctx, tt.args.instanceIDs...) - if !tt.want.isErr(err) { - t.Errorf("ProjectionHandler.prepareBulkStmts() error = %v", err) - return - } - if limitExceeded != tt.want.limitExceeded { - t.Errorf("ProjectionHandler.prepareBulkStmts() = %v, want %v", limitExceeded, tt.want.limitExceeded) - } - }) - } -} - -func TestProjection_subscribe(t *testing.T) { - type args struct { - ctx context.Context - } - type fields struct { - reduce Reduce - update Update - events []eventstore.Event - } - tests := []struct { - name string - args args - fields fields - }{ - { - "panic", - args{ - ctx: context.Background(), - }, - fields{ - reduce: nil, - update: nil, - events: []eventstore.Event{ - newTestEvent("id", "", nil), - }, - }, - }, - { - "error", - args{ - ctx: context.Background(), - }, - fields{ - reduce: testReduceErr(ErrReduce), - update: nil, - events: []eventstore.Event{ - newTestEvent("id", "", nil), - }, - }, - }, - { - "not all statement", - args{ - ctx: context.Background(), - }, - fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, 0, ErrSomeStmtsFailed), - events: []eventstore.Event{ - newTestEvent("id", "", nil), - }, - }, - }, - { - "single event ok", - args{ - ctx: context.Background(), - }, - fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, 1, nil), - events: []eventstore.Event{ - newTestEvent("id", "", nil), - }, - }, - }, - { - "multiple events ok", - args{ - ctx: context.Background(), - }, - fields{ - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 2, 2, nil), - events: []eventstore.Event{ - newTestEvent("id", "", nil), - newTestEvent("id2", "", nil), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &ProjectionHandler{ - Handler: Handler{ - EventQueue: make(chan eventstore.Event, 10), - }, - reduce: tt.fields.reduce, - update: tt.fields.update, - } - ctx, cancel := context.WithCancel(tt.args.ctx) - go func() { - //changed go h.subscribe(ctx) to this to be able to ignore logs easily - t.Helper() - h.subscribe(ctx) - }() - for _, event := range tt.fields.events { - h.EventQueue <- event - } - time.Sleep(1 * time.Second) - cancel() - }) - } -} - -func TestProjection_schedule(t *testing.T) { - - now := func() time.Time { - return time.Date(2023, 1, 31, 12, 0, 0, 0, time.UTC) - } - - type args struct { - ctx context.Context - } - type fields struct { - reduce Reduce - update Update - eventstore func(t *testing.T) *eventstore.Eventstore - lock *lockMock - unlock *unlockMock - query SearchQuery - handleActiveInstances time.Duration - } - type want struct { - locksCount int - lockCanceled bool - unlockCount int - } - tests := []struct { - name string - args args - fields fields - want want - }{ - { - "panic", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return nil - }, - }, - want{}, - }, - { - "filter succeeded once error", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEventsError(ErrFilter), - ), - ) - }, - handleActiveInstances: 2 * time.Minute, - }, - want{ - locksCount: 0, - lockCanceled: false, - unlockCount: 0, - }, - }, - { - "filter instance ids error", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - AggregateType: "system", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "", - Type: "system.projections.scheduler.succeeded", - }). - ExpectInstanceIDsError(ErrFilter), - ), - ) - }, - handleActiveInstances: 2 * time.Minute, - }, - want{ - locksCount: 0, - lockCanceled: false, - unlockCount: 0, - }, - }, - { - "lock error", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - AggregateType: "system", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "", - Type: "system.projections.scheduler.succeeded", - }).ExpectInstanceIDs(nil, "instanceID1"), - ), - ) - }, - lock: &lockMock{ - errWait: 100 * time.Millisecond, - firstErr: ErrLock, - canceled: make(chan bool, 1), - }, - handleActiveInstances: 2 * time.Minute, - }, - want{ - locksCount: 1, - lockCanceled: true, - unlockCount: 0, - }, - }, - { - "trigger error", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents( - &repository.Event{ - AggregateType: "system", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "", - Type: "system.projections.scheduler.succeeded", - }).ExpectInstanceIDs(nil, "instanceID1"), - ), - ) - }, - lock: &lockMock{ - canceled: make(chan bool, 1), - firstErr: nil, - errWait: 100 * time.Millisecond, - }, - unlock: &unlockMock{}, - query: testQuery(nil, 0, ErrQuery), - handleActiveInstances: 2 * time.Minute, - }, - want{ - locksCount: 1, - lockCanceled: true, - unlockCount: 1, - }, - }, - { - "only active instances are handled", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t). - ExpectFilterEvents(&repository.Event{ - AggregateType: "system", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "", - Type: "system.projections.scheduler.succeeded", - }). - ExpectInstanceIDs( - []*repository.Filter{{ - Field: repository.FieldInstanceID, - Operation: repository.OperationNotIn, - Value: database.StringArray{""}, - }, { - Field: repository.FieldCreationDate, - Operation: repository.OperationGreater, - Value: now().Add(-2 * time.Minute), - }}, - "206626268110651755", - ). - ExpectFilterEvents(&repository.Event{ - AggregateType: "quota", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "206626268110651755", - Type: "quota.notificationdue", - }), - )) - }, - lock: &lockMock{ - canceled: make(chan bool, 1), - firstErr: nil, - errWait: 100 * time.Millisecond, - }, - unlock: &unlockMock{}, - handleActiveInstances: 2 * time.Minute, - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, 1, nil), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 2, - nil, - ), - }, - want{ - locksCount: 1, - lockCanceled: false, - unlockCount: 1, - }, - }, - { - "all instances are handled", - args{ - ctx: context.Background(), - }, - fields{ - eventstore: func(t *testing.T) *eventstore.Eventstore { - return eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t). - ExpectFilterEvents(&repository.Event{ - AggregateType: "system", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "", - Type: "system.projections.scheduler.succeeded", - }). - ExpectInstanceIDs([]*repository.Filter{{ - Field: repository.FieldInstanceID, - Operation: repository.OperationNotIn, - Value: database.StringArray{""}, - }, { - Field: repository.FieldCreationDate, - Operation: repository.OperationGreater, - Value: now().Add(-45 * time.Hour), - }}, "206626268110651755"). - ExpectFilterEvents(&repository.Event{ - AggregateType: "quota", - Sequence: 6, - PreviousAggregateSequence: 5, - InstanceID: "206626268110651755", - Type: "quota.notificationdue", - }), - )) - }, - lock: &lockMock{ - canceled: make(chan bool, 1), - firstErr: nil, - errWait: 100 * time.Millisecond, - }, - unlock: &unlockMock{}, - handleActiveInstances: 45 * time.Hour, - reduce: testReduce(newTestStatement("aggregate1", 1, 0)), - update: testUpdate(t, 1, 1, nil), - query: testQuery( - eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes("test"). - Builder(), - 2, - nil, - ), - }, - want{ - locksCount: 1, - lockCanceled: false, - unlockCount: 1, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &ProjectionHandler{ - Handler: Handler{ - EventQueue: make(chan eventstore.Event, 10), - Eventstore: tt.fields.eventstore(t), - }, - reduce: tt.fields.reduce, - update: tt.fields.update, - searchQuery: tt.fields.query, - lock: tt.fields.lock.lock(), - unlock: tt.fields.unlock.unlock(), - triggerProjection: time.NewTimer(0), // immediately run an iteration - requeueAfter: time.Hour, // run only one iteration - concurrentInstances: 1, - handleActiveInstances: tt.fields.handleActiveInstances, - retries: 0, - nowFunc: now, - } - ctx, cancel := context.WithCancel(tt.args.ctx) - go func() { - //changed go h.schedule(ctx) to this to be able to ignore logs easily - t.Helper() - h.schedule(ctx) - }() - - time.Sleep(time.Second) - cancel() - if tt.fields.lock != nil { - tt.fields.lock.check(t, tt.want.locksCount, tt.want.lockCanceled) - } - if tt.fields.unlock != nil { - tt.fields.unlock.check(t, tt.want.unlockCount) - } - }) - } -} - -func Test_cancelOnErr(t *testing.T) { - type args struct { - ctx context.Context - errs chan error - err error - } - tests := []struct { - name string - args args - cancelMocker *cancelMocker - }{ - { - name: "error occured", - args: args{ - ctx: context.Background(), - errs: make(chan error), - err: ErrNoCondition, - }, - cancelMocker: &cancelMocker{ - shouldBeCalled: true, - wasCalled: make(chan bool, 1), - }, - }, - { - name: "ctx done", - args: args{ - ctx: canceledCtx(), - errs: make(chan error), - }, - cancelMocker: &cancelMocker{ - shouldBeCalled: false, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - h := &ProjectionHandler{} - go h.cancelOnErr(tt.args.ctx, tt.args.errs, tt.cancelMocker.mockCancel) - if tt.args.err != nil { - tt.args.errs <- tt.args.err - } - tt.cancelMocker.check(t) - }) - } -} - -func newTestStatement(aggType eventstore.AggregateType, seq, previousSeq uint64) *Statement { - return &Statement{ - AggregateType: aggType, - Sequence: seq, - PreviousSequence: previousSeq, - } -} - -// testEvent implements the Event interface -type testEvent struct { - eventstore.BaseEvent - - description string - data func() interface{} -} - -func newTestEvent(id, description string, data func() interface{}) *testEvent { - return &testEvent{ - description: description, - data: data, - BaseEvent: *eventstore.NewBaseEventForPush( - service.WithService(authz.NewMockContext("instanceID", "resourceOwner", "editorUser"), "editorService"), - eventstore.NewAggregate(authz.NewMockContext("zitadel", "caos", "adlerhurst"), id, "test.aggregate", "v1"), - "test.event", - ), - } -} - -func (e *testEvent) Data() interface{} { - if e.data == nil { - return nil - } - return e.data() -} - -func (e *testEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return nil -} - -func testUpdate(t *testing.T, expectedStmtCount, returnedIndex int, returnedErr error) Update { - return func(ctx context.Context, stmts []*Statement, reduce Reduce) (int, error) { - if expectedStmtCount != len(stmts) { - t.Errorf("expected %d stmts got %d", expectedStmtCount, len(stmts)) - } - return returnedIndex, returnedErr - } -} - -func testReduce(stmts *Statement) Reduce { - return func(event eventstore.Event) (*Statement, error) { - return stmts, nil - } -} - -func testReduceErr(err error) Reduce { - return func(event eventstore.Event) (*Statement, error) { - return nil, err - } -} - -func testQuery(query *eventstore.SearchQueryBuilder, limit uint64, err error) SearchQuery { - return func(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) { - return query, limit, err - } -} - -type lockMock struct { - callCount int - canceled chan bool - mu sync.Mutex - - firstErr error - err error - errWait time.Duration -} - -func (m *lockMock) lock() Lock { - return func(ctx context.Context, _ time.Duration, _ ...string) <-chan error { - m.mu.Lock() - defer m.mu.Unlock() - m.callCount++ - errs := make(chan error) - go func() { - for i := 0; ; i++ { - select { - case <-ctx.Done(): - m.canceled <- true - close(errs) - return - case <-time.After(m.errWait): - err := m.err - if i == 0 { - err = m.firstErr - } - errs <- err - } - } - }() - return errs - } -} - -func (m *lockMock) check(t *testing.T, callCount int, shouldBeCanceled bool) { - t.Helper() - m.mu.Lock() - defer m.mu.Unlock() - if callCount != m.callCount { - t.Errorf("wrong call count: expected %v got: %v", callCount, m.callCount) - } - if shouldBeCanceled { - select { - case <-m.canceled: - case <-time.After(5 * time.Second): - t.Error("lock should be canceled but wasn't") - } - } -} - -type unlockMock struct { - callCount int - err error - mu sync.Mutex -} - -func (m *unlockMock) unlock() Unlock { - return func(...string) error { - m.mu.Lock() - defer m.mu.Unlock() - m.callCount++ - return m.err - } -} - -func (m *unlockMock) check(t *testing.T, callCount int) { - t.Helper() - m.mu.Lock() - defer m.mu.Unlock() - if callCount != m.callCount { - t.Errorf("wrong call count: expected %v got: %v", callCount, m.callCount) - } -} - -func canceledCtx() context.Context { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - return ctx -} - -type cancelMocker struct { - shouldBeCalled bool - wasCalled chan bool -} - -func (m *cancelMocker) mockCancel() { - m.wasCalled <- true -} - -func (m *cancelMocker) check(t *testing.T) { - t.Helper() - if m.shouldBeCalled { - if wasCalled := <-m.wasCalled; !wasCalled { - t.Errorf("cancel: should: %t got: %t", m.shouldBeCalled, wasCalled) - } - } -} diff --git a/internal/eventstore/handler/init.go b/internal/eventstore/handler/init.go index 150196216..0ae6d2261 100644 --- a/internal/eventstore/handler/init.go +++ b/internal/eventstore/handler/init.go @@ -2,7 +2,7 @@ package handler import "context" -//Init initializes the projection with the given check +// Init initializes the projection with the given check type Init func(context.Context, *Check) error type Check struct { diff --git a/internal/eventstore/handler/reduce.go b/internal/eventstore/handler/reduce.go deleted file mode 100644 index 329c59525..000000000 --- a/internal/eventstore/handler/reduce.go +++ /dev/null @@ -1,17 +0,0 @@ -package handler - -import "github.com/zitadel/zitadel/internal/eventstore" - -//EventReducer represents the required data -//to work with events -type EventReducer struct { - Event eventstore.EventType - Reduce Reduce -} - -//EventReducer represents the required data -//to work with aggregates -type AggregateReducer struct { - Aggregate eventstore.AggregateType - EventRedusers []EventReducer -} diff --git a/internal/eventstore/handler/statement.go b/internal/eventstore/handler/statement.go index 1eecd78f2..fe7317d99 100644 --- a/internal/eventstore/handler/statement.go +++ b/internal/eventstore/handler/statement.go @@ -2,77 +2,8 @@ package handler import ( "database/sql" - "encoding/json" - "errors" - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/eventstore" ) -var ( - ErrNoProjection = errors.New("no projection") - ErrNoValues = errors.New("no values") - ErrNoCondition = errors.New("no condition") - ErrSomeStmtsFailed = errors.New("some statements failed") -) - -type Statements []Statement - -func (stmts Statements) Len() int { return len(stmts) } -func (stmts Statements) Swap(i, j int) { stmts[i], stmts[j] = stmts[j], stmts[i] } -func (stmts Statements) Less(i, j int) bool { return stmts[i].Sequence < stmts[j].Sequence } - -type Statement struct { - AggregateType eventstore.AggregateType - Sequence uint64 - PreviousSequence uint64 - InstanceID string - - Execute func(ex Executer, projectionName string) error -} - -func (s *Statement) IsNoop() bool { - return s.Execute == nil -} - type Executer interface { Exec(string, ...interface{}) (sql.Result, error) } - -type Column struct { - Name string - Value interface{} - ParameterOpt func(string) string -} - -func NewCol(name string, value interface{}) Column { - return Column{ - Name: name, - Value: value, - } -} - -func NewJSONCol(name string, value interface{}) Column { - marshalled, err := json.Marshal(value) - if err != nil { - logging.WithFields("column", name).WithError(err).Panic("unable to marshal column") - } - - return NewCol(name, marshalled) -} - -type Condition func(param string) (string, interface{}) - -type NamespacedCondition func(namespace string) Condition - -func NewCond(name string, value interface{}) Condition { - return func(param string) (string, interface{}) { - return name + " = " + param, value - } -} - -func NewNamespacedCondition(name string, value interface{}) NamespacedCondition { - return func(namespace string) Condition { - return NewCond(namespace+"."+name, value) - } -} diff --git a/internal/eventstore/handler/v2/event.go b/internal/eventstore/handler/v2/event.go new file mode 100644 index 000000000..f4b722504 --- /dev/null +++ b/internal/eventstore/handler/v2/event.go @@ -0,0 +1,52 @@ +package handler + +import ( + "context" + + "github.com/zitadel/zitadel/internal/eventstore" +) + +const ( + schedulerSucceeded = eventstore.EventType("system.projections.scheduler.succeeded") + aggregateType = eventstore.AggregateType("system") + aggregateID = "SYSTEM" +) + +func (h *Handler) didProjectionInitialize(ctx context.Context) bool { + events, err := h.es.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + InstanceID(""). + AddQuery(). + AggregateTypes(aggregateType). + AggregateIDs(aggregateID). + EventTypes(schedulerSucceeded). + EventData(map[string]interface{}{ + "name": h.projection.Name(), + }). + Builder(), + ) + return len(events) > 0 && err == nil +} + +func (h *Handler) setSucceededOnce(ctx context.Context) error { + _, err := h.es.Push(ctx, &ProjectionSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush(ctx, + eventstore.NewAggregate(ctx, aggregateID, aggregateType, "v1"), + schedulerSucceeded, + ), + Name: h.projection.Name(), + }) + return err +} + +type ProjectionSucceededEvent struct { + eventstore.BaseEvent `json:"-"` + Name string `json:"name"` +} + +func (p *ProjectionSucceededEvent) Payload() interface{} { + return p +} + +func (p *ProjectionSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return nil +} diff --git a/internal/eventstore/handler/v2/failed_event.go b/internal/eventstore/handler/v2/failed_event.go new file mode 100644 index 000000000..68771042e --- /dev/null +++ b/internal/eventstore/handler/v2/failed_event.go @@ -0,0 +1,95 @@ +package handler + +import ( + "database/sql" + _ "embed" + "time" + + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed failed_event_set.sql + setFailedEventStmt string + //go:embed failed_event_get_count.sql + failureCountStmt string +) + +type failure struct { + sequence uint64 + instance string + aggregateID string + aggregateType eventstore.AggregateType + eventDate time.Time + err error +} + +func failureFromEvent(event eventstore.Event, err error) *failure { + return &failure{ + sequence: event.Sequence(), + instance: event.Aggregate().InstanceID, + aggregateID: event.Aggregate().ID, + aggregateType: event.Aggregate().Type, + eventDate: event.CreatedAt(), + err: err, + } +} + +func failureFromStatement(statement *Statement, err error) *failure { + return &failure{ + sequence: statement.Sequence, + instance: statement.InstanceID, + aggregateID: statement.AggregateID, + aggregateType: statement.AggregateType, + eventDate: statement.CreationDate, + err: err, + } +} + +func (h *Handler) handleFailedStmt(tx *sql.Tx, currentState *state, f *failure) (shouldContinue bool) { + failureCount, err := h.failureCount(tx, f) + if err != nil { + h.logFailure(f).WithError(err).Warn("unable to get failure count") + return false + } + failureCount += 1 + err = h.setFailureCount(tx, failureCount, f) + h.logFailure(f).OnError(err).Warn("unable to update failure count") + + return failureCount >= h.maxFailureCount +} + +func (h *Handler) failureCount(tx *sql.Tx, f *failure) (count uint8, err error) { + row := tx.QueryRow(failureCountStmt, + h.projection.Name(), + f.instance, + f.aggregateType, + f.aggregateID, + f.sequence, + ) + if err = row.Err(); err != nil { + return 0, errors.ThrowInternal(err, "CRDB-Unnex", "unable to update failure count") + } + if err = row.Scan(&count); err != nil { + return 0, errors.ThrowInternal(err, "CRDB-RwSMV", "unable to scan count") + } + return count, nil +} + +func (h *Handler) setFailureCount(tx *sql.Tx, count uint8, f *failure) error { + _, err := tx.Exec(setFailedEventStmt, + h.projection.Name(), + f.instance, + f.aggregateType, + f.aggregateID, + f.eventDate, + f.sequence, + count, + f.err.Error(), + ) + if err != nil { + return errors.ThrowInternal(err, "CRDB-4Ht4x", "set failure count failed") + } + return nil +} diff --git a/internal/eventstore/handler/v2/failed_event_get_count.sql b/internal/eventstore/handler/v2/failed_event_get_count.sql new file mode 100644 index 000000000..18ad7c550 --- /dev/null +++ b/internal/eventstore/handler/v2/failed_event_get_count.sql @@ -0,0 +1,12 @@ +WITH failures AS ( + SELECT + failure_count + FROM + projections.failed_events2 + WHERE + projection_name = $1 + AND instance_id = $2 + AND aggregate_type = $3 + AND aggregate_id = $4 + AND failed_sequence = $5 +) SELECT COALESCE((SELECT failure_count FROM failures), 0) AS failure_count \ No newline at end of file diff --git a/internal/eventstore/handler/v2/failed_event_set.sql b/internal/eventstore/handler/v2/failed_event_set.sql new file mode 100644 index 000000000..b4954299f --- /dev/null +++ b/internal/eventstore/handler/v2/failed_event_set.sql @@ -0,0 +1,31 @@ +INSERT INTO projections.failed_events2 ( + projection_name + , instance_id + , aggregate_type + , aggregate_id + , event_creation_date + , failed_sequence + , failure_count + , error + , last_failed +) VALUES ( + $1 + , $2 + , $3 + , $4 + , $5 + , $6 + , $7 + , $8 + , now() +) ON CONFLICT ( + projection_name + , aggregate_type + , aggregate_id + , failed_sequence + , instance_id +) DO UPDATE SET + failure_count = EXCLUDED.failure_count + , error = EXCLUDED.error + , last_failed = EXCLUDED.last_failed +; \ No newline at end of file diff --git a/internal/eventstore/handler/v2/handler.go b/internal/eventstore/handler/v2/handler.go new file mode 100644 index 000000000..0903dcca7 --- /dev/null +++ b/internal/eventstore/handler/v2/handler.go @@ -0,0 +1,465 @@ +package handler + +import ( + "context" + "database/sql" + "errors" + "math" + "sync" + "time" + + "github.com/jackc/pgconn" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/call" + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/pseudo" + "github.com/zitadel/zitadel/internal/telemetry/tracing" +) + +type EventStore interface { + InstanceIDs(ctx context.Context, maxAge time.Duration, forceLoad bool, query *eventstore.SearchQueryBuilder) ([]string, error) + Filter(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) + Push(ctx context.Context, cmds ...eventstore.Command) ([]eventstore.Event, error) +} + +type Config struct { + Client *database.DB + Eventstore EventStore + + BulkLimit uint16 + RequeueEvery time.Duration + RetryFailedAfter time.Duration + HandleActiveInstances time.Duration + TransactionDuration time.Duration + MaxFailureCount uint8 + + TriggerWithoutEvents Reduce +} + +type Handler struct { + client *database.DB + projection Projection + + es EventStore + bulkLimit uint16 + eventTypes map[eventstore.AggregateType][]eventstore.EventType + + maxFailureCount uint8 + retryFailedAfter time.Duration + requeueEvery time.Duration + handleActiveInstances time.Duration + txDuration time.Duration + now nowFunc + + triggeredInstancesSync sync.Map + + triggerWithoutEvents Reduce +} + +// nowFunc makes [time.Now] mockable +type nowFunc func() time.Time + +type Projection interface { + Name() string + Reducers() []AggregateReducer +} + +func NewHandler( + ctx context.Context, + config *Config, + projection Projection, +) *Handler { + aggregates := make(map[eventstore.AggregateType][]eventstore.EventType, len(projection.Reducers())) + for _, reducer := range projection.Reducers() { + eventTypes := make([]eventstore.EventType, len(reducer.EventReducers)) + for i, eventReducer := range reducer.EventReducers { + eventTypes[i] = eventReducer.Event + } + if _, ok := aggregates[reducer.Aggregate]; ok { + aggregates[reducer.Aggregate] = append(aggregates[reducer.Aggregate], eventTypes...) + continue + } + aggregates[reducer.Aggregate] = eventTypes + } + + handler := &Handler{ + projection: projection, + client: config.Client, + es: config.Eventstore, + bulkLimit: config.BulkLimit, + eventTypes: aggregates, + requeueEvery: config.RequeueEvery, + handleActiveInstances: config.HandleActiveInstances, + now: time.Now, + maxFailureCount: config.MaxFailureCount, + retryFailedAfter: config.RetryFailedAfter, + triggeredInstancesSync: sync.Map{}, + triggerWithoutEvents: config.TriggerWithoutEvents, + txDuration: config.TransactionDuration, + } + + return handler +} + +func (h *Handler) Start(ctx context.Context) { + go h.schedule(ctx) + if h.triggerWithoutEvents != nil { + return + } + go h.subscribe(ctx) +} + +func (h *Handler) schedule(ctx context.Context) { + // if there was no run before trigger instantly + t := time.NewTimer(0) + didInitialize := h.didProjectionInitialize(ctx) + if didInitialize { + t.Reset(h.requeueEvery) + } + + for { + select { + case <-ctx.Done(): + t.Stop() + return + case <-t.C: + instances, err := h.queryInstances(ctx, didInitialize) + h.log().OnError(err).Debug("unable to query instances") + + var instanceFailed bool + scheduledCtx := call.WithTimestamp(ctx) + for _, instance := range instances { + instanceCtx := authz.WithInstanceID(scheduledCtx, instance) + + // simple implementation of do while + _, err = h.Trigger(instanceCtx) + instanceFailed = instanceFailed || err != nil + h.log().WithField("instance", instance).OnError(err).Info("scheduled trigger failed") + // retry if trigger failed + for ; err != nil; _, err = h.Trigger(instanceCtx) { + time.Sleep(h.retryFailedAfter) + instanceFailed = instanceFailed || err != nil + h.log().WithField("instance", instance).OnError(err).Info("scheduled trigger failed") + if err == nil { + break + } + } + } + + if !didInitialize && !instanceFailed { + err = h.setSucceededOnce(ctx) + h.log().OnError(err).Debug("unable to set succeeded once") + didInitialize = err == nil + } + t.Reset(h.requeueEvery) + } + } +} + +func (h *Handler) subscribe(ctx context.Context) { + queue := make(chan eventstore.Event, 100) + subscription := eventstore.SubscribeEventTypes(queue, h.eventTypes) + for { + select { + case <-ctx.Done(): + subscription.Unsubscribe() + h.log().Debug("shutdown") + return + case event := <-queue: + events := checkAdditionalEvents(queue, event) + solvedInstances := make([]string, 0, len(events)) + queueCtx := call.WithTimestamp(ctx) + for _, e := range events { + if instanceSolved(solvedInstances, e.Aggregate().InstanceID) { + continue + } + queueCtx = authz.WithInstanceID(queueCtx, e.Aggregate().InstanceID) + _, err := h.Trigger(queueCtx) + h.log().OnError(err).Debug("trigger of queued event failed") + if err == nil { + solvedInstances = append(solvedInstances, e.Aggregate().InstanceID) + } + } + } + } +} + +func instanceSolved(solvedInstances []string, instanceID string) bool { + for _, solvedInstance := range solvedInstances { + if solvedInstance == instanceID { + return true + } + } + return false +} + +func checkAdditionalEvents(eventQueue chan eventstore.Event, event eventstore.Event) []eventstore.Event { + events := make([]eventstore.Event, 1) + events[0] = event + for { + wait := time.NewTimer(1 * time.Millisecond) + select { + case event := <-eventQueue: + events = append(events, event) + case <-wait.C: + return events + } + } +} + +func (h *Handler) queryInstances(ctx context.Context, didInitialize bool) ([]string, error) { + query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs). + AwaitOpenTransactions(). + AllowTimeTravel(). + ExcludedInstanceID("") + if didInitialize { + query = query. + CreationDateAfter(h.now().Add(-1 * h.handleActiveInstances)) + } + return h.es.InstanceIDs(ctx, h.requeueEvery, !didInitialize, query) +} + +type triggerConfig struct { + awaitRunning bool +} + +type triggerOpt func(conf *triggerConfig) + +func WithAwaitRunning() triggerOpt { + return func(conf *triggerConfig) { + conf.awaitRunning = true + } +} + +func (h *Handler) Trigger(ctx context.Context, opts ...triggerOpt) (_ context.Context, err error) { + if authz.GetInstance(ctx).InstanceID() != "" { + var span *tracing.Span + ctx, span = tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + } + + config := new(triggerConfig) + for _, opt := range opts { + opt(config) + } + + cancel := h.lockInstance(ctx, config) + if cancel == nil { + return call.ResetTimestamp(ctx), nil + } + defer cancel() + + for i := 0; ; i++ { + additionalIteration, err := h.processEvents(ctx, config) + h.log().OnError(err).Warn("process events failed") + h.log().WithField("iteration", i).Debug("trigger iteration") + if !additionalIteration || err != nil { + return call.ResetTimestamp(ctx), err + } + } +} + +// lockInstances tries to lock the instance. +// If the instance is already locked from another process no cancel function is returned +// the instance can be skipped then +// If the instance is locked, an unlock deferable function is returned +func (h *Handler) lockInstance(ctx context.Context, config *triggerConfig) func() { + instanceID := authz.GetInstance(ctx).InstanceID() + + // Check that the instance has a mutex to lock + instanceMu, _ := h.triggeredInstancesSync.LoadOrStore(instanceID, new(sync.Mutex)) + unlock := func() { + instanceMu.(*sync.Mutex).Unlock() + } + if !instanceMu.(*sync.Mutex).TryLock() { + instanceMu.(*sync.Mutex).Lock() + if config.awaitRunning { + return unlock + } + defer unlock() + return nil + } + return unlock +} + +func (h *Handler) processEvents(ctx context.Context, config *triggerConfig) (additionalIteration bool, err error) { + defer func() { + pgErr := new(pgconn.PgError) + if errors.As(err, &pgErr) { + // error returned if the row is currently locked by another connection + if pgErr.Code == "55P03" { + h.log().Debug("state already locked") + err = nil + additionalIteration = false + } + } + }() + + if h.txDuration > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, h.txDuration) + defer cancel() + } + + tx, err := h.client.Begin() + if err != nil { + return false, err + } + defer func() { + if err != nil { + rollbackErr := tx.Rollback() + h.log().OnError(rollbackErr).Debug("unable to rollback tx") + return + } + err = tx.Commit() + }() + + currentState, err := h.currentState(ctx, tx, config) + if err != nil { + if errors.Is(err, errJustUpdated) { + return false, nil + } + return additionalIteration, err + } + + var statements []*Statement + statements, additionalIteration, err = h.generateStatements(ctx, tx, currentState) + if err != nil || len(statements) == 0 { + return additionalIteration, err + } + + lastProcessedIndex, err := h.executeStatements(ctx, tx, currentState, statements) + if lastProcessedIndex < 0 { + return false, err + } + + currentState.position = statements[lastProcessedIndex].Position + currentState.aggregateID = statements[lastProcessedIndex].AggregateID + currentState.aggregateType = statements[lastProcessedIndex].AggregateType + currentState.sequence = statements[lastProcessedIndex].Sequence + currentState.eventTimestamp = statements[lastProcessedIndex].CreationDate + err = h.setState(tx, currentState) + + return additionalIteration, err +} + +func (h *Handler) generateStatements(ctx context.Context, tx *sql.Tx, currentState *state) (_ []*Statement, additionalIteration bool, err error) { + if h.triggerWithoutEvents != nil { + stmt, err := h.triggerWithoutEvents(pseudo.NewScheduledEvent(ctx, time.Now(), currentState.instanceID)) + if err != nil { + return nil, false, err + } + return []*Statement{stmt}, false, nil + } + + events, err := h.es.Filter(ctx, h.eventQuery(currentState)) + if err != nil { + h.log().WithError(err).Debug("filter eventstore failed") + return nil, false, err + } + eventAmount := len(events) + events = skipPreviouslyReduced(events, currentState) + + if len(events) == 0 { + h.updateLastUpdated(ctx, tx, currentState) + return nil, false, nil + } + + statements, err := h.eventsToStatements(tx, events, currentState) + if len(statements) == 0 { + return nil, false, err + } + + additionalIteration = eventAmount == int(h.bulkLimit) + if len(statements) < len(events) { + // retry imediatly if statements failed + additionalIteration = true + } + + return statements, additionalIteration, nil +} + +func skipPreviouslyReduced(events []eventstore.Event, currentState *state) []eventstore.Event { + for i, event := range events { + if event.Position() == currentState.position && + event.Aggregate().ID == currentState.aggregateID && + event.Aggregate().Type == currentState.aggregateType && + event.Sequence() == currentState.sequence { + return events[i+1:] + } + } + return events +} + +func (h *Handler) executeStatements(ctx context.Context, tx *sql.Tx, currentState *state, statements []*Statement) (lastProcessedIndex int, err error) { + lastProcessedIndex = -1 + + for i, statement := range statements { + select { + case <-ctx.Done(): + break + default: + err := h.executeStatement(ctx, tx, currentState, statement) + if err != nil { + return lastProcessedIndex, err + } + lastProcessedIndex = i + } + } + return lastProcessedIndex, nil +} + +func (h *Handler) executeStatement(ctx context.Context, tx *sql.Tx, currentState *state, statement *Statement) (err error) { + if statement.Execute == nil { + return nil + } + + _, err = tx.Exec("SAVEPOINT exec") + if err != nil { + h.log().WithError(err).Debug("create savepoint failed") + return err + } + var shouldContinue bool + defer func() { + _, err = tx.Exec("RELEASE SAVEPOINT exec") + }() + + if err = statement.Execute(tx, h.projection.Name()); err != nil { + h.log().WithError(err).Error("statement execution failed") + + shouldContinue = h.handleFailedStmt(tx, currentState, failureFromStatement(statement, err)) + if shouldContinue { + return nil + } + + return err + } + + return nil +} + +func (h *Handler) eventQuery(currentState *state) *eventstore.SearchQueryBuilder { + builder := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). + Limit(uint64(h.bulkLimit)). + AllowTimeTravel(). + OrderAsc(). + InstanceID(currentState.instanceID) + + if currentState.position > 0 { + builder = builder.PositionAfter(math.Float64frombits(math.Float64bits(currentState.position) - 10)) + } + + for aggregateType, eventTypes := range h.eventTypes { + query := builder. + AddQuery(). + AggregateTypes(aggregateType). + EventTypes(eventTypes...) + + builder = query.Builder() + } + + return builder +} diff --git a/internal/eventstore/handler/crdb/init.go b/internal/eventstore/handler/v2/init.go similarity index 83% rename from internal/eventstore/handler/crdb/init.go rename to internal/eventstore/handler/v2/init.go index 420b11731..cca5cb995 100644 --- a/internal/eventstore/handler/crdb/init.go +++ b/internal/eventstore/handler/v2/init.go @@ -1,4 +1,4 @@ -package crdb +package handler import ( "context" @@ -9,19 +9,19 @@ import ( "github.com/jackc/pgconn" "github.com/zitadel/logging" - caos_errs "github.com/zitadel/zitadel/internal/errors" + errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/handler" ) type Table struct { - columns []*Column + columns []*InitColumn primaryKey PrimaryKey indices []*Index constraints []*Constraint foreignKeys []*ForeignKey } -func NewTable(columns []*Column, key PrimaryKey, opts ...TableOption) *Table { +func NewTable(columns []*InitColumn, key PrimaryKey, opts ...TableOption) *Table { t := &Table{ columns: columns, primaryKey: key, @@ -37,7 +37,7 @@ type SuffixedTable struct { suffix string } -func NewSuffixedTable(columns []*Column, key PrimaryKey, suffix string, opts ...TableOption) *SuffixedTable { +func NewSuffixedTable(columns []*InitColumn, key PrimaryKey, suffix string, opts ...TableOption) *SuffixedTable { return &SuffixedTable{ Table: *NewTable(columns, key, opts...), suffix: suffix, @@ -64,7 +64,7 @@ func WithForeignKey(key *ForeignKey) TableOption { } } -type Column struct { +type InitColumn struct { Name string Type ColumnType nullable bool @@ -72,10 +72,10 @@ type Column struct { deleteCascade string } -type ColumnOption func(*Column) +type ColumnOption func(*InitColumn) -func NewColumn(name string, columnType ColumnType, opts ...ColumnOption) *Column { - column := &Column{ +func NewColumn(name string, columnType ColumnType, opts ...ColumnOption) *InitColumn { + column := &InitColumn{ Name: name, Type: columnType, nullable: false, @@ -88,19 +88,19 @@ func NewColumn(name string, columnType ColumnType, opts ...ColumnOption) *Column } func Nullable() ColumnOption { - return func(c *Column) { + return func(c *InitColumn) { c.nullable = true } } func Default(value interface{}) ColumnOption { - return func(c *Column) { + return func(c *InitColumn) { c.defaultValue = value } } func DeleteCascade(column string) ColumnOption { - return func(c *Column) { + return func(c *InitColumn) { c.deleteCascade = column } } @@ -128,9 +128,8 @@ const ( func NewIndex(name string, columns []string, opts ...indexOpts) *Index { i := &Index{ - Name: name, - Columns: columns, - bucketCount: 0, + Name: name, + Columns: columns, } for _, opt := range opts { opt(i) @@ -139,16 +138,16 @@ func NewIndex(name string, columns []string, opts ...indexOpts) *Index { } type Index struct { - Name string - Columns []string - bucketCount uint16 + Name string + Columns []string + includes []string } type indexOpts func(*Index) -func Hash(bucketsCount uint16) indexOpts { +func WithInclude(columns ...string) indexOpts { return func(i *Index) { - i.bucketCount = bucketsCount + i.includes = columns } } @@ -186,25 +185,28 @@ type ForeignKey struct { RefColumns []string } -// Init implements handler.Init -func (h *StatementHandler) Init(ctx context.Context) error { - check := h.initCheck - if check == nil || check.IsNoop() { +type initializer interface { + Init() *handler.Check +} + +func (h *Handler) Init(ctx context.Context) error { + check, ok := h.projection.(initializer) + if !ok || check.Init().IsNoop() { return nil } tx, err := h.client.BeginTx(ctx, nil) if err != nil { - return caos_errs.ThrowInternal(err, "CRDB-SAdf2", "begin failed") + return errs.ThrowInternal(err, "CRDB-SAdf2", "begin failed") } - for i, execute := range check.Executes { - logging.WithFields("projection", h.ProjectionName, "execute", i).Debug("executing check") - next, err := execute(h.client, h.ProjectionName) + for i, execute := range check.Init().Executes { + logging.WithFields("projection", h.projection.Name(), "execute", i).Debug("executing check") + next, err := execute(tx, h.projection.Name()) if err != nil { - tx.Rollback() + logging.OnError(tx.Rollback()).Debug("unable to rollback") return err } if !next { - logging.WithFields("projection", h.ProjectionName, "execute", i).Debug("projection set up") + logging.WithFields("projection", h.projection.Name(), "execute", i).Debug("projection set up") break } } @@ -272,15 +274,15 @@ func execNextIfExists(config execConfig, q query, opts []execOption, executeNext } func isErrAlreadyExists(err error) bool { - caosErr := &caos_errs.CaosError{} + caosErr := &errs.CaosError{} if !errors.As(err, &caosErr) { return false } - sqlErr, ok := caosErr.GetParent().(*pgconn.PgError) - if !ok { - return false + pgErr := new(pgconn.PgError) + if errors.As(caosErr.Parent, &pgErr) { + return pgErr.Code == "42P07" } - return sqlErr.Code == "42P07" + return false } func createTableStatement(table *Table, tableName string, suffix string) string { @@ -330,11 +332,10 @@ func createIndexStatement(index *Index, tableName string) string { tableName, strings.Join(index.Columns, ","), ) - if index.bucketCount == 0 { - return stmt + ";" + if len(index.includes) > 0 { + stmt += " INCLUDE (" + strings.Join(index.includes, ", ") + ")" } - return fmt.Sprintf("SET experimental_enable_hash_sharded_indexes=on; %s USING HASH WITH BUCKET_COUNT = %d;", - stmt, index.bucketCount) + return stmt + ";" } func foreignKeyName(name, tableName, suffix string) string { @@ -355,7 +356,7 @@ func tableNameWithoutSchema(name string) string { return name[strings.LastIndex(name, ".")+1:] } -func createColumnsStatement(cols []*Column, tableName string) string { +func createColumnsStatement(cols []*InitColumn, tableName string) string { columns := make([]string, len(cols)) for i, col := range cols { column := col.Name + " " + columnType(col.Type) diff --git a/internal/eventstore/handler/v2/log.go b/internal/eventstore/handler/v2/log.go new file mode 100644 index 000000000..e9145dfb4 --- /dev/null +++ b/internal/eventstore/handler/v2/log.go @@ -0,0 +1,23 @@ +package handler + +import ( + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/eventstore" +) + +func (h *Handler) log() *logging.Entry { + return logging.WithFields("projection", h.projection.Name()) +} + +func (h *Handler) logFailure(fail *failure) *logging.Entry { + return h.log().WithField("sequence", fail.sequence). + WithField("instance", fail.instance). + WithField("aggregate", fail.aggregateID) +} + +func (h *Handler) logEvent(event eventstore.Event) *logging.Entry { + return h.log().WithField("sequence", event.Sequence()). + WithField("instance", event.Aggregate().InstanceID). + WithField("aggregate", event.Aggregate().Type) +} diff --git a/internal/eventstore/handler/v2/mock_test.go b/internal/eventstore/handler/v2/mock_test.go new file mode 100644 index 000000000..ebd49659f --- /dev/null +++ b/internal/eventstore/handler/v2/mock_test.go @@ -0,0 +1,18 @@ +package handler + +var _ Projection = (*projection)(nil) + +type projection struct { + name string + reducers []AggregateReducer +} + +// Name implements Projection +func (p *projection) Name() string { + return p.name +} + +// Reducers implements Projection +func (p *projection) Reducers() []AggregateReducer { + return p.reducers +} diff --git a/internal/eventstore/handler/v2/reduce.go b/internal/eventstore/handler/v2/reduce.go new file mode 100644 index 000000000..467b12cba --- /dev/null +++ b/internal/eventstore/handler/v2/reduce.go @@ -0,0 +1,21 @@ +package handler + +import "github.com/zitadel/zitadel/internal/eventstore" + +// EventReducer represents the required data +// to work with events +type EventReducer struct { + Event eventstore.EventType + Reduce Reduce +} + +// Reduce reduces the given event to a statement +// which is used to update the projection +type Reduce func(eventstore.Event) (*Statement, error) + +// EventReducer represents the required data +// to work with aggregates +type AggregateReducer struct { + Aggregate eventstore.AggregateType + EventReducers []EventReducer +} diff --git a/internal/eventstore/handler/v2/state.go b/internal/eventstore/handler/v2/state.go new file mode 100644 index 000000000..a5171be8f --- /dev/null +++ b/internal/eventstore/handler/v2/state.go @@ -0,0 +1,119 @@ +package handler + +import ( + "context" + "database/sql" + _ "embed" + "errors" + "time" + + "github.com/zitadel/zitadel/internal/api/authz" + errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +type state struct { + instanceID string + position float64 + eventTimestamp time.Time + aggregateType eventstore.AggregateType + aggregateID string + sequence uint64 +} + +var ( + //go:embed state_get.sql + currentStateStmt string + //go:embed state_get_await.sql + currentStateAwaitStmt string + //go:embed state_set.sql + updateStateStmt string + //go:embed state_lock.sql + lockStateStmt string + //go:embed state_set_last_run.sql + updateStateLastRunStmt string + + errJustUpdated = errors.New("projection was just updated") +) + +func (h *Handler) currentState(ctx context.Context, tx *sql.Tx, config *triggerConfig) (currentState *state, err error) { + currentState = &state{ + instanceID: authz.GetInstance(ctx).InstanceID(), + } + + var ( + aggregateID = new(sql.NullString) + aggregateType = new(sql.NullString) + sequence = new(sql.NullInt64) + timestamp = new(sql.NullTime) + position = new(sql.NullFloat64) + ) + + stateQuery := currentStateStmt + if config.awaitRunning { + stateQuery = currentStateAwaitStmt + } + + row := tx.QueryRow(stateQuery, currentState.instanceID, h.projection.Name()) + err = row.Scan( + aggregateID, + aggregateType, + sequence, + timestamp, + position, + ) + if errors.Is(err, sql.ErrNoRows) { + err = h.lockState(tx, currentState.instanceID) + } + if err != nil { + h.log().WithError(err).Debug("unable to query current state") + return nil, err + } + + currentState.aggregateID = aggregateID.String + currentState.aggregateType = eventstore.AggregateType(aggregateType.String) + currentState.sequence = uint64(sequence.Int64) + currentState.eventTimestamp = timestamp.Time + currentState.position = position.Float64 + return currentState, nil +} + +func (h *Handler) setState(tx *sql.Tx, updatedState *state) error { + res, err := tx.Exec(updateStateStmt, + h.projection.Name(), + updatedState.instanceID, + updatedState.aggregateID, + updatedState.aggregateType, + updatedState.sequence, + updatedState.eventTimestamp, + updatedState.position, + ) + if err != nil { + h.log().WithError(err).Debug("unable to update state") + return err + } + if affected, err := res.RowsAffected(); affected == 0 { + h.log().OnError(err).Error("unable to check if states are updated") + return errs.ThrowInternal(err, "V2-FGEKi", "unable to update state") + } + return nil +} + +func (h *Handler) updateLastUpdated(ctx context.Context, tx *sql.Tx, updatedState *state) { + _, err := tx.ExecContext(ctx, updateStateLastRunStmt, h.projection.Name(), updatedState.instanceID) + h.log().OnError(err).Debug("unable to update last updated") +} + +func (h *Handler) lockState(tx *sql.Tx, instanceID string) error { + res, err := tx.Exec(lockStateStmt, + h.projection.Name(), + instanceID, + ) + if err != nil { + return err + } + if affected, err := res.RowsAffected(); affected == 0 || err != nil { + return errs.ThrowInternal(err, "V2-lpiK0", "projection already locked") + } + return nil +} diff --git a/internal/eventstore/handler/v2/state_get.sql b/internal/eventstore/handler/v2/state_get.sql new file mode 100644 index 000000000..37e0c8784 --- /dev/null +++ b/internal/eventstore/handler/v2/state_get.sql @@ -0,0 +1,12 @@ +SELECT + aggregate_id + , aggregate_type + , "sequence" + , event_date + , "position" +FROM + projections.current_states +WHERE + instance_id = $1 + AND projection_name = $2 +FOR UPDATE NOWAIT; \ No newline at end of file diff --git a/internal/eventstore/handler/v2/state_get_await.sql b/internal/eventstore/handler/v2/state_get_await.sql new file mode 100644 index 000000000..0ab4bc06c --- /dev/null +++ b/internal/eventstore/handler/v2/state_get_await.sql @@ -0,0 +1,12 @@ +SELECT + aggregate_id + , aggregate_type + , "sequence" + , event_date + , "position" +FROM + projections.current_states +WHERE + instance_id = $1 + AND projection_name = $2 +FOR UPDATE; \ No newline at end of file diff --git a/internal/eventstore/handler/v2/state_lock.sql b/internal/eventstore/handler/v2/state_lock.sql new file mode 100644 index 000000000..55888e7f3 --- /dev/null +++ b/internal/eventstore/handler/v2/state_lock.sql @@ -0,0 +1,9 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , last_updated +) VALUES ( + $1 + , $2 + , now() +) ON CONFLICT DO NOTHING; \ No newline at end of file diff --git a/internal/eventstore/handler/v2/state_set.sql b/internal/eventstore/handler/v2/state_set.sql new file mode 100644 index 000000000..3a80bc200 --- /dev/null +++ b/internal/eventstore/handler/v2/state_set.sql @@ -0,0 +1,29 @@ +INSERT INTO projections.current_states ( + projection_name + , instance_id + , aggregate_id + , aggregate_type + , "sequence" + , event_date + , "position" + , last_updated +) VALUES ( + $1 + , $2 + , $3 + , $4 + , $5 + , $6 + , $7 + , now() +) ON CONFLICT ( + projection_name + , instance_id +) DO UPDATE SET + aggregate_id = $3 + , aggregate_type = $4 + , "sequence" = $5 + , event_date = $6 + , "position" = $7 + , last_updated = statement_timestamp() +; \ No newline at end of file diff --git a/internal/eventstore/handler/v2/state_set_last_run.sql b/internal/eventstore/handler/v2/state_set_last_run.sql new file mode 100644 index 000000000..e632c47ba --- /dev/null +++ b/internal/eventstore/handler/v2/state_set_last_run.sql @@ -0,0 +1,2 @@ +UPDATE projections.current_states SET last_updated = now() WHERE projection_name = $1 AND instance_id = $2; + \ No newline at end of file diff --git a/internal/eventstore/handler/v2/state_test.go b/internal/eventstore/handler/v2/state_test.go new file mode 100644 index 000000000..943219066 --- /dev/null +++ b/internal/eventstore/handler/v2/state_test.go @@ -0,0 +1,447 @@ +package handler + +import ( + "context" + "database/sql" + "database/sql/driver" + _ "embed" + "errors" + "reflect" + "testing" + "time" + + "github.com/jackc/pgconn" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/database/mock" + errs "github.com/zitadel/zitadel/internal/errors" +) + +func TestHandler_lockState(t *testing.T) { + type fields struct { + projection Projection + mock *mock.SQLMock + } + type args struct { + instanceID string + } + tests := []struct { + name string + fields fields + args args + isErr func(t *testing.T, err error) + }{ + { + name: "tx closed", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec( + lockStateStmt, + mock.WithExecArgs( + "projection", + "instance", + ), + mock.WithExecErr(sql.ErrTxDone), + ), + ), + }, + args: args{ + instanceID: "instance", + }, + isErr: func(t *testing.T, err error) { + if !errors.Is(err, sql.ErrTxDone) { + t.Errorf("unexpected error, want: %v got: %v", sql.ErrTxDone, err) + } + }, + }, + { + name: "no rows affeced", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec( + lockStateStmt, + mock.WithExecArgs( + "projection", + "instance", + ), + mock.WithExecNoRowsAffected(), + ), + ), + }, + args: args{ + instanceID: "instance", + }, + isErr: func(t *testing.T, err error) { + if !errors.Is(err, errs.ThrowInternal(nil, "V2-lpiK0", "")) { + t.Errorf("unexpected error: want internal (V2lpiK0), got: %v", err) + } + }, + }, + { + name: "rows affected", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec( + lockStateStmt, + mock.WithExecArgs( + "projection", + "instance", + ), + mock.WithExecRowsAffected(1), + ), + ), + }, + args: args{ + instanceID: "instance", + }, + }, + } + for _, tt := range tests { + if tt.isErr == nil { + tt.isErr = func(t *testing.T, err error) { + if err != nil { + t.Error("expected no error got:", err) + } + } + } + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + projection: tt.fields.projection, + } + + tx, err := tt.fields.mock.DB.Begin() + if err != nil { + t.Fatalf("unable to begin transaction: %v", err) + } + + err = h.lockState(tx, tt.args.instanceID) + tt.isErr(t, err) + + tt.fields.mock.Assert(t) + }) + } +} + +func TestHandler_updateLastUpdated(t *testing.T) { + type fields struct { + projection Projection + mock *mock.SQLMock + } + type args struct { + updatedState *state + } + tests := []struct { + name string + fields fields + args args + isErr func(t *testing.T, err error) + }{ + { + name: "update fails", + fields: fields{ + projection: &projection{ + name: "instance", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec(updateStateStmt, + mock.WithExecErr(sql.ErrTxDone), + ), + ), + }, + args: args{ + updatedState: &state{ + instanceID: "instance", + eventTimestamp: time.Now(), + position: 42, + }, + }, + isErr: func(t *testing.T, err error) { + if !errors.Is(err, sql.ErrTxDone) { + t.Errorf("unexpected error, want: %v, got %v", sql.ErrTxDone, err) + } + }, + }, + { + name: "no rows affected", + fields: fields{ + projection: &projection{ + name: "instance", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec(updateStateStmt, + mock.WithExecNoRowsAffected(), + ), + ), + }, + args: args{ + updatedState: &state{ + instanceID: "instance", + eventTimestamp: time.Now(), + position: 42, + }, + }, + isErr: func(t *testing.T, err error) { + if !errors.Is(err, errs.ThrowInternal(nil, "V2-FGEKi", "")) { + t.Errorf("unexpected error, want: %v, got %v", sql.ErrTxDone, err) + } + }, + }, + { + name: "success", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExcpectExec(updateStateStmt, + mock.WithExecArgs( + "projection", + "instance", + "aggregate id", + "aggregate type", + uint64(42), + mock.AnyType[time.Time]{}, + float64(42), + ), + mock.WithExecRowsAffected(1), + ), + ), + }, + args: args{ + updatedState: &state{ + instanceID: "instance", + eventTimestamp: time.Now(), + position: 42, + aggregateType: "aggregate type", + aggregateID: "aggregate id", + sequence: 42, + }, + }, + }, + } + for _, tt := range tests { + if tt.isErr == nil { + tt.isErr = func(t *testing.T, err error) { + if err != nil { + t.Error("expected no error got:", err) + } + } + } + t.Run(tt.name, func(t *testing.T) { + tx, err := tt.fields.mock.DB.Begin() + if err != nil { + t.Fatalf("unable to begin transaction: %v", err) + } + + h := &Handler{ + projection: tt.fields.projection, + } + err = h.setState(tx, tt.args.updatedState) + + tt.isErr(t, err) + tt.fields.mock.Assert(t) + }) + } +} + +func TestHandler_currentState(t *testing.T) { + testTime := time.Now() + type fields struct { + projection Projection + mock *mock.SQLMock + } + type args struct { + ctx context.Context + } + type want struct { + currentState *state + isErr func(t *testing.T, err error) + } + tests := []struct { + name string + fields fields + args args + want want + }{ + { + name: "connection done", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExpectQuery(currentStateStmt, + mock.WithQueryArgs( + "instance", + "projection", + ), + mock.WithQueryErr(sql.ErrConnDone), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance"), + }, + want: want{ + isErr: func(t *testing.T, err error) { + if !errors.Is(err, sql.ErrConnDone) { + t.Errorf("unexpected error, want: %v, got: %v", sql.ErrConnDone, err) + } + }, + }, + }, + { + name: "no row but lock err", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExpectQuery(currentStateStmt, + mock.WithQueryArgs( + "instance", + "projection", + ), + mock.WithQueryErr(sql.ErrNoRows), + ), + mock.ExcpectExec(lockStateStmt, + mock.WithExecArgs( + "projection", + "instance", + ), + mock.WithExecErr(sql.ErrTxDone), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance"), + }, + want: want{ + isErr: func(t *testing.T, err error) { + if !errors.Is(err, sql.ErrTxDone) { + t.Errorf("unexpected error, want: %v, got: %v", sql.ErrTxDone, err) + } + }, + }, + }, + { + name: "state locked", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExpectQuery(currentStateStmt, + mock.WithQueryArgs( + "instance", + "projection", + ), + mock.WithQueryErr(&pgconn.PgError{Code: "55P03"}), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance"), + }, + want: want{ + isErr: func(t *testing.T, err error) { + pgErr := new(pgconn.PgError) + if !errors.As(err, &pgErr) { + t.Errorf("error should be PgErr but was %T", err) + return + } + if pgErr.Code != "55P03" { + t.Errorf("expected code 55P03 got: %s", pgErr.Code) + } + }, + }, + }, + { + name: "success", + fields: fields{ + projection: &projection{ + name: "projection", + }, + mock: mock.NewSQLMock(t, + mock.ExpectBegin(nil), + mock.ExpectQuery(currentStateStmt, + mock.WithQueryArgs( + "instance", + "projection", + ), + mock.WithQueryResult( + []string{"aggregate_id", "aggregate_type", "event_sequence", "event_date", "position"}, + [][]driver.Value{ + { + "aggregate id", + "aggregate type", + int64(42), + testTime, + float64(42), + }, + }, + ), + ), + ), + }, + args: args{ + ctx: authz.WithInstanceID(context.Background(), "instance"), + }, + want: want{ + currentState: &state{ + instanceID: "instance", + eventTimestamp: testTime, + position: 42, + aggregateType: "aggregate type", + aggregateID: "aggregate id", + sequence: 42, + }, + }, + }, + } + for _, tt := range tests { + if tt.want.isErr == nil { + tt.want.isErr = func(t *testing.T, err error) { + if err != nil { + t.Error("expected no error got:", err) + } + } + } + t.Run(tt.name, func(t *testing.T) { + h := &Handler{ + projection: tt.fields.projection, + } + + tx, err := tt.fields.mock.DB.Begin() + if err != nil { + t.Fatalf("unable to begin transaction: %v", err) + } + + gotCurrentState, err := h.currentState(tt.args.ctx, tx, new(triggerConfig)) + + tt.want.isErr(t, err) + if !reflect.DeepEqual(gotCurrentState, tt.want.currentState) { + t.Errorf("Handler.currentState() gotCurrentState = %v, want %v", gotCurrentState, tt.want.currentState) + } + tt.fields.mock.Assert(t) + }) + } +} diff --git a/internal/eventstore/handler/crdb/statement.go b/internal/eventstore/handler/v2/statement.go similarity index 52% rename from internal/eventstore/handler/crdb/statement.go rename to internal/eventstore/handler/v2/statement.go index bc1d50d0f..bea3c156a 100644 --- a/internal/eventstore/handler/crdb/statement.go +++ b/internal/eventstore/handler/v2/statement.go @@ -1,41 +1,89 @@ -package crdb +package handler import ( "database/sql" - "errors" + "encoding/json" + errs "errors" "strconv" "strings" + "time" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/database" - zitadel_errors "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" ) -type execOption func(*execConfig) -type execConfig struct { - tableName string - - args []interface{} - err error - ignoreNotFound bool +func (h *Handler) eventsToStatements(tx *sql.Tx, events []eventstore.Event, currentState *state) (statements []*Statement, err error) { + statements = make([]*Statement, 0, len(events)) + for _, event := range events { + statement, err := h.reduce(event) + if err != nil { + h.logEvent(event).WithError(err).Error("reduce failed") + if shouldContinue := h.handleFailedStmt(tx, currentState, failureFromEvent(event, err)); shouldContinue { + continue + } + return statements, err + } + statements = append(statements, statement) + } + return statements, nil } +func (h *Handler) reduce(event eventstore.Event) (*Statement, error) { + for _, reducer := range h.projection.Reducers() { + if reducer.Aggregate != event.Aggregate().Type { + continue + } + for _, reduce := range reducer.EventReducers { + if reduce.Event != event.Type() { + continue + } + return reduce.Reduce(event) + } + } + return NewNoOpStatement(event), nil +} + +type Statement struct { + AggregateType eventstore.AggregateType + AggregateID string + Sequence uint64 + Position float64 + CreationDate time.Time + InstanceID string + + Execute Exec +} + +type Exec func(ex Executer, projectionName string) error + func WithTableSuffix(name string) func(*execConfig) { return func(o *execConfig) { o.tableName += "_" + name } } -func WithIgnoreNotFound() func(*execConfig) { - return func(o *execConfig) { - o.ignoreNotFound = true +var ( + ErrNoProjection = errs.New("no projection") + ErrNoValues = errs.New("no values") + ErrNoCondition = errs.New("no condition") +) + +func NewStatement(event eventstore.Event, e Exec) *Statement { + return &Statement{ + AggregateType: event.Aggregate().Type, + Sequence: event.Sequence(), + Position: event.Position(), + AggregateID: event.Aggregate().ID, + CreationDate: event.CreatedAt(), + InstanceID: event.Aggregate().InstanceID, + Execute: e, } } -func NewCreateStatement(event eventstore.Event, values []handler.Column, opts ...execOption) *handler.Statement { +func NewCreateStatement(event eventstore.Event, values []Column, opts ...execOption) *Statement { cols, params, args := columnsToQuery(values) columnNames := strings.Join(cols, ", ") valuesPlaceholder := strings.Join(params, ", ") @@ -45,23 +93,17 @@ func NewCreateStatement(event eventstore.Event, values []handler.Column, opts .. } if len(values) == 0 { - config.err = handler.ErrNoValues + config.err = ErrNoValues } q := func(config execConfig) string { return "INSERT INTO " + config.tableName + " (" + columnNames + ") VALUES (" + valuesPlaceholder + ")" } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: exec(config, q, opts), - } + return NewStatement(event, exec(config, q, opts)) } -func NewUpsertStatement(event eventstore.Event, conflictCols []handler.Column, values []handler.Column, opts ...execOption) *handler.Statement { +func NewUpsertStatement(event eventstore.Event, conflictCols []Column, values []Column, opts ...execOption) *Statement { cols, params, args := columnsToQuery(values) conflictTarget := make([]string, len(conflictCols)) @@ -74,12 +116,12 @@ func NewUpsertStatement(event eventstore.Event, conflictCols []handler.Column, v } if len(values) == 0 { - config.err = handler.ErrNoValues + config.err = ErrNoValues } updateCols, updateVals := getUpdateCols(cols, conflictTarget) if len(updateCols) == 0 || len(updateVals) == 0 { - config.err = handler.ErrNoValues + config.err = ErrNoValues } q := func(config execConfig) string { @@ -96,13 +138,7 @@ func NewUpsertStatement(event eventstore.Event, conflictCols []handler.Column, v " ON CONFLICT (" + strings.Join(conflictTarget, ", ") + ") DO " + updateStmt } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: exec(config, q, opts), - } + return NewStatement(event, exec(config, q, opts)) } func getUpdateCols(cols, conflictTarget []string) (updateCols, updateVals []string) { @@ -132,9 +168,9 @@ func getUpdateCols(cols, conflictTarget []string) (updateCols, updateVals []stri return updateCols, updateVals } -func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditions []handler.Condition, opts ...execOption) *handler.Statement { +func NewUpdateStatement(event eventstore.Event, values []Column, conditions []Condition, opts ...execOption) *Statement { cols, params, args := columnsToQuery(values) - wheres, whereArgs := conditionsToWhere(conditions, len(args)) + wheres, whereArgs := conditionsToWhere(conditions, len(args)+1) args = append(args, whereArgs...) config := execConfig{ @@ -142,11 +178,11 @@ func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditi } if len(values) == 0 { - config.err = handler.ErrNoValues + config.err = ErrNoValues } if len(conditions) == 0 { - config.err = handler.ErrNoCondition + config.err = ErrNoCondition } q := func(config execConfig) string { @@ -159,17 +195,11 @@ func NewUpdateStatement(event eventstore.Event, values []handler.Column, conditi return "UPDATE " + config.tableName + " SET (" + strings.Join(cols, ", ") + ") = (" + strings.Join(params, ", ") + ") WHERE " + strings.Join(wheres, " AND ") } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: exec(config, q, opts), - } + return NewStatement(event, exec(config, q, opts)) } -func NewDeleteStatement(event eventstore.Event, conditions []handler.Condition, opts ...execOption) *handler.Statement { - wheres, args := conditionsToWhere(conditions, 0) +func NewDeleteStatement(event eventstore.Event, conditions []Condition, opts ...execOption) *Statement { + wheres, args := conditionsToWhere(conditions, 1) wheresPlaceholders := strings.Join(wheres, " AND ") @@ -178,32 +208,21 @@ func NewDeleteStatement(event eventstore.Event, conditions []handler.Condition, } if len(conditions) == 0 { - config.err = handler.ErrNoCondition + config.err = ErrNoCondition } q := func(config execConfig) string { return "DELETE FROM " + config.tableName + " WHERE " + wheresPlaceholders } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: exec(config, q, opts), - } + return NewStatement(event, exec(config, q, opts)) } -func NewNoOpStatement(event eventstore.Event) *handler.Statement { - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - } +func NewNoOpStatement(event eventstore.Event) *Statement { + return NewStatement(event, nil) } -func NewMultiStatement(event eventstore.Event, opts ...func(eventstore.Event) Exec) *handler.Statement { +func NewMultiStatement(event eventstore.Event, opts ...func(eventstore.Event) Exec) *Statement { if len(opts) == 0 { return NewNoOpStatement(event) } @@ -211,43 +230,47 @@ func NewMultiStatement(event eventstore.Event, opts ...func(eventstore.Event) Ex for i, opt := range opts { execs[i] = opt(event) } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: multiExec(execs), + return NewStatement(event, multiExec(execs)) +} + +func AddNoOpStatement() func(eventstore.Event) Exec { + return func(event eventstore.Event) Exec { + return NewNoOpStatement(event).Execute } } -type Exec func(ex handler.Executer, projectionName string) error - -func AddCreateStatement(columns []handler.Column, opts ...execOption) func(eventstore.Event) Exec { +func AddCreateStatement(columns []Column, opts ...execOption) func(eventstore.Event) Exec { return func(event eventstore.Event) Exec { return NewCreateStatement(event, columns, opts...).Execute } } -func AddUpsertStatement(indexCols []handler.Column, values []handler.Column, opts ...execOption) func(eventstore.Event) Exec { +func AddUpsertStatement(indexCols []Column, values []Column, opts ...execOption) func(eventstore.Event) Exec { return func(event eventstore.Event) Exec { return NewUpsertStatement(event, indexCols, values, opts...).Execute } } -func AddUpdateStatement(values []handler.Column, conditions []handler.Condition, opts ...execOption) func(eventstore.Event) Exec { +func AddUpdateStatement(values []Column, conditions []Condition, opts ...execOption) func(eventstore.Event) Exec { return func(event eventstore.Event) Exec { return NewUpdateStatement(event, values, conditions, opts...).Execute } } -func AddDeleteStatement(conditions []handler.Condition, opts ...execOption) func(eventstore.Event) Exec { +func AddDeleteStatement(conditions []Condition, opts ...execOption) func(eventstore.Event) Exec { return func(event eventstore.Event) Exec { return NewDeleteStatement(event, conditions, opts...).Execute } } -func NewArrayAppendCol(column string, value interface{}) handler.Column { - return handler.Column{ +func AddCopyStatement(conflict, from, to []Column, conditions []NamespacedCondition, opts ...execOption) func(eventstore.Event) Exec { + return func(event eventstore.Event) Exec { + return NewCopyStatement(event, conflict, from, to, conditions, opts...).Execute + } +} + +func NewArrayAppendCol(column string, value interface{}) Column { + return Column{ Name: column, Value: value, ParameterOpt: func(placeholder string) string { @@ -256,8 +279,8 @@ func NewArrayAppendCol(column string, value interface{}) handler.Column { } } -func NewArrayRemoveCol(column string, value interface{}) handler.Column { - return handler.Column{ +func NewArrayRemoveCol(column string, value interface{}) Column { + return Column{ Name: column, Value: value, ParameterOpt: func(placeholder string) string { @@ -266,15 +289,15 @@ func NewArrayRemoveCol(column string, value interface{}) handler.Column { } } -func NewArrayIntersectCol(column string, value interface{}) handler.Column { +func NewArrayIntersectCol(column string, value interface{}) Column { var arrayType string switch value.(type) { - case []string, database.StringArray: + case []string, database.TextArray[string]: arrayType = "TEXT" //TODO: handle more types if necessary } - return handler.Column{ + return Column{ Name: column, Value: value, ParameterOpt: func(placeholder string) string { @@ -283,38 +306,10 @@ func NewArrayIntersectCol(column string, value interface{}) handler.Column { } } -func NewCopyCol(column, from string) handler.Column { - return handler.Column{ +func NewCopyCol(column, from string) Column { + return Column{ Name: column, - Value: handler.NewCol(from, nil), - } -} - -func NewLessThanCond(column string, value interface{}) handler.Condition { - return func(param string) (string, interface{}) { - return column + " < " + param, value - } -} - -func NewIsNullCond(column string) handler.Condition { - return func(param string) (string, interface{}) { - return column + " IS NULL", nil - } -} - -// NewTextArrayContainsCond returns a handler.Condition that checks if the column that stores an array of text contains the given value -func NewTextArrayContainsCond(column string, value string) handler.Condition { - return func(param string) (string, interface{}) { - return column + " @> " + param, database.StringArray{value} - } -} - -// Not is a function and not a method, so that calling it is well readable -// For example conditions := []handler.Condition{ Not(NewTextArrayContainsCond())} -func Not(condition handler.Condition) handler.Condition { - return func(param string) (string, interface{}) { - cond, value := condition(param) - return "NOT (" + cond + ")", value + Value: NewCol(from, nil), } } @@ -323,7 +318,7 @@ func Not(condition handler.Condition) handler.Condition { // if the value of a col is empty the data will be copied from the selected row // if the value of a col is not empty the data will be set by the static value // conds represent the conditions for the selection subquery -func NewCopyStatement(event eventstore.Event, conflictCols, from, to []handler.Column, nsCond []handler.NamespacedCondition, opts ...execOption) *handler.Statement { +func NewCopyStatement(event eventstore.Event, conflictCols, from, to []Column, nsCond []NamespacedCondition, opts ...execOption) *Statement { columnNames := make([]string, len(to)) selectColumns := make([]string, len(from)) updateColumns := make([]string, len(columnNames)) @@ -342,11 +337,11 @@ func NewCopyStatement(event eventstore.Event, conflictCols, from, to []handler.C } } - cond := make([]handler.Condition, len(nsCond)) + cond := make([]Condition, len(nsCond)) for i := range nsCond { cond[i] = nsCond[i]("copy_table") } - wheres, values := conditionsToWhere(cond, len(args)) + wheres, values := conditionsToWhere(cond, len(args)+1) args = append(args, values...) conflictTargets := make([]string, len(conflictCols)) @@ -359,11 +354,11 @@ func NewCopyStatement(event eventstore.Event, conflictCols, from, to []handler.C } if len(from) == 0 || len(to) == 0 || len(from) != len(to) { - config.err = handler.ErrNoValues + config.err = ErrNoValues } if len(cond) == 0 { - config.err = handler.ErrNoCondition + config.err = ErrNoCondition } q := func(config execConfig) string { @@ -385,23 +380,17 @@ func NewCopyStatement(event eventstore.Event, conflictCols, from, to []handler.C ")" } - return &handler.Statement{ - AggregateType: event.Aggregate().Type, - Sequence: event.Sequence(), - PreviousSequence: event.PreviousAggregateTypeSequence(), - InstanceID: event.Aggregate().InstanceID, - Execute: exec(config, q, opts), - } + return NewStatement(event, exec(config, q, opts)) } -func columnsToQuery(cols []handler.Column) (names []string, parameters []string, values []interface{}) { +func columnsToQuery(cols []Column) (names []string, parameters []string, values []interface{}) { names = make([]string, len(cols)) values = make([]interface{}, len(cols)) parameters = make([]string, len(cols)) var parameterIndex int for i, col := range cols { names[i] = col.Name - if c, ok := col.Value.(handler.Column); ok { + if c, ok := col.Value.(Column); ok { parameters[i] = c.Name continue } else { @@ -416,25 +405,105 @@ func columnsToQuery(cols []handler.Column) (names []string, parameters []string, return names, parameters, values[:parameterIndex] } -func conditionsToWhere(conditions []handler.Condition, paramOffset int) (wheres []string, values []interface{}) { - wheres = make([]string, len(conditions)) - values = make([]interface{}, 0, len(conditions)) - for i, conditionFunc := range conditions { - condition, value := conditionFunc("$" + strconv.Itoa(i+1+paramOffset)) - wheres[i] = "(" + condition + ")" - if value != nil { - values = append(values, value) - } +func conditionsToWhere(conds []Condition, paramOffset int) (wheres []string, values []interface{}) { + wheres = make([]string, len(conds)) + values = make([]any, 0, len(conds)) + + for i, cond := range conds { + var args []any + wheres[i], args = cond("$" + strconv.Itoa(paramOffset)) + paramOffset += len(args) + values = append(values, args...) + wheres[i] = "(" + wheres[i] + ")" } + return wheres, values } +type Column struct { + Name string + Value interface{} + ParameterOpt func(string) string +} + +func NewCol(name string, value interface{}) Column { + return Column{ + Name: name, + Value: value, + } +} + +func NewJSONCol(name string, value interface{}) Column { + marshalled, err := json.Marshal(value) + if err != nil { + logging.WithFields("column", name).WithError(err).Panic("unable to marshal column") + } + + return NewCol(name, marshalled) +} + +type Condition func(param string) (string, []any) + +type NamespacedCondition func(namespace string) Condition + +func NewCond(name string, value interface{}) Condition { + return func(param string) (string, []any) { + return name + " = " + param, []any{value} + } +} + +func NewNamespacedCondition(name string, value interface{}) NamespacedCondition { + return func(namespace string) Condition { + return NewCond(namespace+"."+name, value) + } +} + +func NewLessThanCond(column string, value interface{}) Condition { + return func(param string) (string, []any) { + return column + " < " + param, []any{value} + } +} + +func NewIsNullCond(column string) Condition { + return func(string) (string, []any) { + return column + " IS NULL", nil + } +} + +// NewTextArrayContainsCond returns a Condition that checks if the column that stores an array of text contains the given value +func NewTextArrayContainsCond(column string, value string) Condition { + return func(param string) (string, []any) { + return column + " @> " + param, []any{database.TextArray[string]{value}} + } +} + +// Not is a function and not a method, so that calling it is well readable +// For example conditions := []Condition{ Not(NewTextArrayContainsCond())} +func Not(condition Condition) Condition { + return func(param string) (string, []any) { + cond, value := condition(param) + return "NOT (" + cond + ")", value + } +} + +type Executer interface { + Exec(string, ...interface{}) (sql.Result, error) +} + +type execOption func(*execConfig) +type execConfig struct { + tableName string + + args []interface{} + err error +} + type query func(config execConfig) string func exec(config execConfig, q query, opts []execOption) Exec { - return func(ex handler.Executer, projectionName string) error { + return func(ex Executer, projectionName string) (err error) { if projectionName == "" { - return handler.ErrNoProjection + return ErrNoProjection } if config.err != nil { @@ -446,12 +515,21 @@ func exec(config execConfig, q query, opts []execOption) Exec { opt(&config) } - if _, err := ex.Exec(q(config), config.args...); err != nil { - if config.ignoreNotFound && errors.Is(err, sql.ErrNoRows) { - logging.WithError(err).Debugf("ignored not found: %v", err) - return nil + _, err = ex.Exec("SAVEPOINT stmt_exec") + if err != nil { + return errors.ThrowInternal(err, "CRDB-YdOXD", "create savepoint failed") + } + defer func() { + if err != nil { + _, rollbackErr := ex.Exec("ROLLBACK TO SAVEPOINT stmt_exec") + logging.OnError(rollbackErr).Debug("rollback failed") + return } - return zitadel_errors.ThrowInternal(err, "CRDB-pKtsr", "exec failed") + _, err = ex.Exec("RELEASE SAVEPOINT stmt_exec") + }() + _, err = ex.Exec(q(config), config.args...) + if err != nil { + return errors.ThrowInternal(err, "CRDB-pKtsr", "exec failed") } return nil @@ -459,8 +537,11 @@ func exec(config execConfig, q query, opts []execOption) Exec { } func multiExec(execList []Exec) Exec { - return func(ex handler.Executer, projectionName string) error { + return func(ex Executer, projectionName string) error { for _, exec := range execList { + if exec == nil { + continue + } if err := exec(ex, projectionName); err != nil { return err } diff --git a/internal/eventstore/handler/crdb/statement_test.go b/internal/eventstore/handler/v2/statement_test.go similarity index 80% rename from internal/eventstore/handler/crdb/statement_test.go rename to internal/eventstore/handler/v2/statement_test.go index e023f0079..940da53b2 100644 --- a/internal/eventstore/handler/crdb/statement_test.go +++ b/internal/eventstore/handler/v2/statement_test.go @@ -1,14 +1,14 @@ -package crdb +package handler import ( "database/sql" "errors" "reflect" + "strings" "testing" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" ) type wantExecuter struct { @@ -24,21 +24,50 @@ type params struct { args []interface{} } -var errTestErr = errors.New("some error") +var errTest = errors.New("some error") + +var _ eventstore.Event = &testEvent{} + +type testEvent struct { + eventstore.BaseEvent + sequence uint64 + previousSequence uint64 + aggregateType eventstore.AggregateType + instanceID string +} + +func (e *testEvent) Sequence() uint64 { + return e.sequence +} + +func (e *testEvent) Aggregate() *eventstore.Aggregate { + return &eventstore.Aggregate{ + Type: e.aggregateType, + InstanceID: e.instanceID, + } +} + +func (e *testEvent) PreviousAggregateTypeSequence() uint64 { + return e.previousSequence +} func (ex *wantExecuter) check(t *testing.T) { t.Helper() - if ex.wasExecuted && !ex.shouldExecute { + switch { + case ex.wasExecuted && !ex.shouldExecute: t.Error("executer should not be executed") - } else if !ex.wasExecuted && ex.shouldExecute { + case !ex.wasExecuted && ex.shouldExecute: t.Error("executer should be executed") - } else if ex.wasExecuted != ex.shouldExecute { + case ex.wasExecuted != ex.shouldExecute: t.Errorf("executed missmatched should be %t, but was %t", ex.shouldExecute, ex.wasExecuted) } } func (ex *wantExecuter) Exec(query string, args ...interface{}) (sql.Result, error) { ex.t.Helper() + if strings.Contains(query, "SAVEPOINT") { + return nil, nil + } ex.wasExecuted = true if ex.i >= len(ex.params) { ex.t.Errorf("did not expect more exec, but got:\n %q with %q", query, args) @@ -59,7 +88,7 @@ func TestNewCreateStatement(t *testing.T) { type args struct { table string event *testEvent - values []handler.Column + values []Column } type want struct { aggregateType eventstore.AggregateType @@ -83,7 +112,7 @@ func TestNewCreateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -99,7 +128,7 @@ func TestNewCreateStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoProjection) + return errors.Is(err, ErrNoProjection) }, }, }, @@ -112,7 +141,7 @@ func TestNewCreateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{}, + values: []Column{}, }, want: want{ table: "my_table", @@ -123,7 +152,7 @@ func TestNewCreateStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoValues) + return errors.Is(err, ErrNoValues) }, }, }, @@ -136,7 +165,7 @@ func TestNewCreateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -181,8 +210,8 @@ func TestNewUpsertStatement(t *testing.T) { type args struct { table string event *testEvent - conflictCols []handler.Column - values []handler.Column + conflictCols []Column + values []Column } type want struct { aggregateType eventstore.AggregateType @@ -206,7 +235,7 @@ func TestNewUpsertStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -222,7 +251,7 @@ func TestNewUpsertStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoProjection) + return errors.Is(err, ErrNoProjection) }, }, }, @@ -235,7 +264,7 @@ func TestNewUpsertStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{}, + values: []Column{}, }, want: want{ table: "my_table", @@ -246,7 +275,7 @@ func TestNewUpsertStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoValues) + return errors.Is(err, ErrNoValues) }, }, }, @@ -259,10 +288,10 @@ func TestNewUpsertStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conflictCols: []handler.Column{ - handler.NewCol("col1", nil), + conflictCols: []Column{ + NewCol("col1", nil), }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -278,7 +307,7 @@ func TestNewUpsertStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoValues) + return errors.Is(err, ErrNoValues) }, }, }, @@ -291,10 +320,10 @@ func TestNewUpsertStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conflictCols: []handler.Column{ - handler.NewCol("col1", nil), + conflictCols: []Column{ + NewCol("col1", nil), }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -337,10 +366,10 @@ func TestNewUpsertStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conflictCols: []handler.Column{ - handler.NewCol("col1", nil), + conflictCols: []Column{ + NewCol("col1", nil), }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -389,8 +418,8 @@ func TestNewUpdateStatement(t *testing.T) { type args struct { table string event *testEvent - values []handler.Column - conditions []handler.Condition + values []Column + conditions []Condition } type want struct { table string @@ -414,14 +443,14 @@ func TestNewUpdateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", }, }, - conditions: []handler.Condition{ - handler.NewCond("col2", 1), + conditions: []Condition{ + NewCond("col2", 1), }, }, want: want{ @@ -433,7 +462,7 @@ func TestNewUpdateStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoProjection) + return errors.Is(err, ErrNoProjection) }, }, }, @@ -446,9 +475,9 @@ func TestNewUpdateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{}, - conditions: []handler.Condition{ - handler.NewCond("col2", 1), + values: []Column{}, + conditions: []Condition{ + NewCond("col2", 1), }, }, want: want{ @@ -460,7 +489,7 @@ func TestNewUpdateStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoValues) + return errors.Is(err, ErrNoValues) }, }, }, @@ -473,13 +502,13 @@ func TestNewUpdateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", }, }, - conditions: []handler.Condition{}, + conditions: []Condition{}, }, want: want{ table: "my_table", @@ -490,7 +519,7 @@ func TestNewUpdateStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoCondition) + return errors.Is(err, ErrNoCondition) }, }, }, @@ -503,14 +532,14 @@ func TestNewUpdateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", }, }, - conditions: []handler.Condition{ - handler.NewCond("col2", 1), + conditions: []Condition{ + NewCond("col2", 1), }, }, want: want{ @@ -541,7 +570,7 @@ func TestNewUpdateStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - values: []handler.Column{ + values: []Column{ { Name: "col1", Value: "val", @@ -551,8 +580,8 @@ func TestNewUpdateStatement(t *testing.T) { Value: "val5", }, }, - conditions: []handler.Condition{ - handler.NewCond("col2", 1), + conditions: []Condition{ + NewCond("col2", 1), }, }, want: want{ @@ -593,7 +622,7 @@ func TestNewDeleteStatement(t *testing.T) { type args struct { table string event *testEvent - conditions []handler.Condition + conditions []Condition } type want struct { @@ -618,8 +647,8 @@ func TestNewDeleteStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conditions: []handler.Condition{ - handler.NewCond("col2", 1), + conditions: []Condition{ + NewCond("col2", 1), }, }, want: want{ @@ -631,7 +660,7 @@ func TestNewDeleteStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoProjection) + return errors.Is(err, ErrNoProjection) }, }, }, @@ -644,7 +673,7 @@ func TestNewDeleteStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conditions: []handler.Condition{}, + conditions: []Condition{}, }, want: want{ table: "my_table", @@ -655,7 +684,7 @@ func TestNewDeleteStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoCondition) + return errors.Is(err, ErrNoCondition) }, }, }, @@ -668,8 +697,8 @@ func TestNewDeleteStatement(t *testing.T) { previousSequence: 0, aggregateType: "agg", }, - conditions: []handler.Condition{ - handler.NewCond("col1", 1), + conditions: []Condition{ + NewCond("col1", 1), }, }, want: want{ @@ -709,11 +738,16 @@ func TestNewDeleteStatement(t *testing.T) { func TestNewNoOpStatement(t *testing.T) { type args struct { event *testEvent + table string + } + type want struct { + executer *wantExecuter + isErr func(error) bool } tests := []struct { name string args args - want *handler.Statement + want want }{ { name: "generate correctly", @@ -725,20 +759,29 @@ func TestNewNoOpStatement(t *testing.T) { instanceID: "instanceID", }, }, - want: &handler.Statement{ - AggregateType: "agg", - Execute: nil, - Sequence: 5, - PreviousSequence: 3, - InstanceID: "instanceID", + want: want{ + executer: nil, + isErr: func(err error) bool { + return err == nil + }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := NewNoOpStatement(tt.args.event); !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewNoOpStatement() = %v, want %v", got, tt.want) + stmt := NewNoOpStatement(tt.args.event) + if tt.want.executer != nil && stmt.Execute == nil { + t.Error("expected executer, but was nil") } + if stmt.Execute == nil { + return + } + tt.want.executer.t = t + err := stmt.Execute(tt.want.executer, tt.args.table) + if !tt.want.isErr(err) { + t.Errorf("unexpected error: %v", err) + } + tt.want.executer.check(t) }) } } @@ -772,10 +815,17 @@ func TestNewMultiStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - execs: nil, + execs: []func(eventstore.Event) Exec{ + AddNoOpStatement(), + }, }, want: want{ - executer: nil, + executer: &wantExecuter{ + shouldExecute: false, + }, + isErr: func(err error) bool { + return err == nil + }, }, }, { @@ -789,10 +839,10 @@ func TestNewMultiStatement(t *testing.T) { }, execs: []func(eventstore.Event) Exec{ AddDeleteStatement( - []handler.Condition{}, + []Condition{}, ), AddCreateStatement( - []handler.Column{ + []Column{ { Name: "col1", Value: 1, @@ -809,7 +859,7 @@ func TestNewMultiStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoCondition) + return errors.Is(err, ErrNoCondition) }, }, }, @@ -824,22 +874,21 @@ func TestNewMultiStatement(t *testing.T) { }, execs: []func(eventstore.Event) Exec{ AddDeleteStatement( - []handler.Condition{ - handler.NewCond("col1", 1), - }, - ), + []Condition{ + NewCond("col1", 1), + }), AddCreateStatement( - []handler.Column{ + []Column{ { Name: "col1", Value: 1, }, }), AddUpsertStatement( - []handler.Column{ - handler.NewCol("col1", nil), + []Column{ + NewCol("col1", nil), }, - []handler.Column{ + []Column{ { Name: "col1", Value: 1, @@ -850,16 +899,15 @@ func TestNewMultiStatement(t *testing.T) { }, }), AddUpdateStatement( - []handler.Column{ + []Column{ { Name: "col1", Value: 1, }, }, - []handler.Condition{ - handler.NewCond("col1", 1), - }, - ), + []Condition{ + NewCond("col1", 1), + }), }, }, want: want{ @@ -918,10 +966,10 @@ func TestNewCopyStatement(t *testing.T) { type args struct { table string event *testEvent - conflictingCols []handler.Column - from []handler.Column - to []handler.Column - conds []handler.NamespacedCondition + conflictingCols []Column + from []Column + to []Column + conds []NamespacedCondition } type want struct { aggregateType eventstore.AggregateType @@ -945,8 +993,8 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conds: []handler.NamespacedCondition{ - handler.NewNamespacedCondition("col2", 1), + conds: []NamespacedCondition{ + NewNamespacedCondition("col2", 1), }, }, want: want{ @@ -958,7 +1006,7 @@ func TestNewCopyStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoProjection) + return errors.Is(err, ErrNoProjection) }, }, }, @@ -971,13 +1019,13 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conds: []handler.NamespacedCondition{}, - from: []handler.Column{ + conds: []NamespacedCondition{}, + from: []Column{ { Name: "col", }, }, - to: []handler.Column{ + to: []Column{ { Name: "col", }, @@ -992,7 +1040,7 @@ func TestNewCopyStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoCondition) + return errors.Is(err, ErrNoCondition) }, }, }, @@ -1005,13 +1053,13 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conds: []handler.NamespacedCondition{}, - from: []handler.Column{ + conds: []NamespacedCondition{}, + from: []Column{ { Name: "col", }, }, - to: []handler.Column{ + to: []Column{ { Name: "col", }, @@ -1029,7 +1077,7 @@ func TestNewCopyStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoCondition) + return errors.Is(err, ErrNoCondition) }, }, }, @@ -1042,10 +1090,10 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - conds: []handler.NamespacedCondition{ - handler.NewNamespacedCondition("col2", nil), + conds: []NamespacedCondition{ + NewNamespacedCondition("col2", nil), }, - from: []handler.Column{}, + from: []Column{}, }, want: want{ table: "my_table", @@ -1056,7 +1104,7 @@ func TestNewCopyStatement(t *testing.T) { shouldExecute: false, }, isErr: func(err error) bool { - return errors.Is(err, handler.ErrNoValues) + return errors.Is(err, ErrNoValues) }, }, }, @@ -1069,7 +1117,7 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - from: []handler.Column{ + from: []Column{ { Name: "state", Value: 1, @@ -1084,7 +1132,7 @@ func TestNewCopyStatement(t *testing.T) { Name: "col_b", }, }, - to: []handler.Column{ + to: []Column{ { Name: "state", }, @@ -1098,9 +1146,9 @@ func TestNewCopyStatement(t *testing.T) { Name: "col_b", }, }, - conds: []handler.NamespacedCondition{ - handler.NewNamespacedCondition("id", 2), - handler.NewNamespacedCondition("state", 3), + conds: []NamespacedCondition{ + NewNamespacedCondition("id", 2), + NewNamespacedCondition("state", 3), }, }, want: want{ @@ -1131,7 +1179,7 @@ func TestNewCopyStatement(t *testing.T) { sequence: 1, previousSequence: 0, }, - from: []handler.Column{ + from: []Column{ { Value: 1, }, @@ -1145,7 +1193,7 @@ func TestNewCopyStatement(t *testing.T) { Name: "col_b", }, }, - to: []handler.Column{ + to: []Column{ { Name: "state", }, @@ -1159,9 +1207,9 @@ func TestNewCopyStatement(t *testing.T) { Name: "col_d", }, }, - conds: []handler.NamespacedCondition{ - handler.NewNamespacedCondition("id", 2), - handler.NewNamespacedCondition("state", 3), + conds: []NamespacedCondition{ + NewNamespacedCondition("id", 2), + NewNamespacedCondition("state", 3), }, }, want: want{ @@ -1200,7 +1248,7 @@ func TestNewCopyStatement(t *testing.T) { func TestStatement_Execute(t *testing.T) { type fields struct { - execute func(ex handler.Executer, projectionName string) error + execute func(ex Executer, projectionName string) error } type want struct { isErr func(error) bool @@ -1217,7 +1265,7 @@ func TestStatement_Execute(t *testing.T) { { name: "execute returns no error", fields: fields{ - execute: func(ex handler.Executer, projectionName string) error { return nil }, + execute: func(ex Executer, projectionName string) error { return nil }, }, args: args{ projectionName: "my_projection", @@ -1234,18 +1282,18 @@ func TestStatement_Execute(t *testing.T) { projectionName: "my_projection", }, fields: fields{ - execute: func(ex handler.Executer, projectionName string) error { return errTestErr }, + execute: func(ex Executer, projectionName string) error { return errTest }, }, want: want{ isErr: func(err error) bool { - return errors.Is(err, errTestErr) + return errors.Is(err, errTest) }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - stmt := &handler.Statement{ + stmt := &Statement{ Execute: tt.fields.execute, } if err := stmt.Execute(nil, tt.args.projectionName); !tt.want.isErr(err) { @@ -1257,7 +1305,7 @@ func TestStatement_Execute(t *testing.T) { func Test_columnsToQuery(t *testing.T) { type args struct { - cols []handler.Column + cols []Column } type want struct { names []string @@ -1281,7 +1329,7 @@ func Test_columnsToQuery(t *testing.T) { { name: "one column", args: args{ - cols: []handler.Column{ + cols: []Column{ { Name: "col1", Value: 1, @@ -1297,7 +1345,7 @@ func Test_columnsToQuery(t *testing.T) { { name: "multiple columns", args: args{ - cols: []handler.Column{ + cols: []Column{ { Name: "col1", Value: 1, @@ -1317,14 +1365,14 @@ func Test_columnsToQuery(t *testing.T) { { name: "with copy column", args: args{ - cols: []handler.Column{ + cols: []Column{ { Name: "col1", Value: 1, }, { Name: "col2", - Value: handler.Column{ + Value: Column{ Name: "col1", }, }, @@ -1357,9 +1405,9 @@ func Test_columnsToQuery(t *testing.T) { } } -func Test_conditionsToWhere(t *testing.T) { +func Test_columnsToWhere(t *testing.T) { type args struct { - conds []handler.Condition + conds []Condition paramOffset int } type want struct { @@ -1382,10 +1430,10 @@ func Test_conditionsToWhere(t *testing.T) { { name: "no offset", args: args{ - conds: []handler.Condition{ - handler.NewCond("col1", "val1"), + conds: []Condition{ + NewCond("col1", "val1"), }, - paramOffset: 0, + paramOffset: 1, }, want: want{ wheres: []string{"(col1 = $1)"}, @@ -1395,11 +1443,11 @@ func Test_conditionsToWhere(t *testing.T) { { name: "multiple cols", args: args{ - conds: []handler.Condition{ - handler.NewCond("col1", "val1"), - handler.NewCond("col2", "val2"), + conds: []Condition{ + NewCond("col1", "val1"), + NewCond("col2", "val2"), }, - paramOffset: 0, + paramOffset: 1, }, want: want{ wheres: []string{"(col1 = $1)", "(col2 = $2)"}, @@ -1409,10 +1457,10 @@ func Test_conditionsToWhere(t *testing.T) { { name: "2 offset", args: args{ - conds: []handler.Condition{ - handler.NewCond("col1", "val1"), + conds: []Condition{ + NewCond("col1", "val1"), }, - paramOffset: 2, + paramOffset: 3, }, want: want{ wheres: []string{"(col1 = $3)"}, @@ -1422,9 +1470,10 @@ func Test_conditionsToWhere(t *testing.T) { { name: "less than", args: args{ - conds: []handler.Condition{ + conds: []Condition{ NewLessThanCond("col1", "val1"), }, + paramOffset: 1, }, want: want{ wheres: []string{"(col1 < $1)"}, @@ -1434,7 +1483,7 @@ func Test_conditionsToWhere(t *testing.T) { { name: "is null", args: args{ - conds: []handler.Condition{ + conds: []Condition{ NewIsNullCond("col1"), }, }, @@ -1446,21 +1495,23 @@ func Test_conditionsToWhere(t *testing.T) { { name: "text array contains", args: args{ - conds: []handler.Condition{ + conds: []Condition{ NewTextArrayContainsCond("col1", "val1"), }, + paramOffset: 1, }, want: want{ wheres: []string{"(col1 @> $1)"}, - values: []interface{}{database.StringArray{"val1"}}, + values: []interface{}{database.TextArray[string]{"val1"}}, }, }, { name: "not", args: args{ - conds: []handler.Condition{ - Not(handler.NewCond("col1", "val1")), + conds: []Condition{ + Not(NewCond("col1", "val1")), }, + paramOffset: 1, }, want: want{ wheres: []string{"(NOT (col1 = $1))"}, @@ -1490,7 +1541,7 @@ func TestParameterOpts(t *testing.T) { tests := []struct { name string args args - constructor func(column string, value interface{}) handler.Column + constructor func(column string, value interface{}) Column want string }{ { @@ -1523,3 +1574,60 @@ func TestParameterOpts(t *testing.T) { }) } } + +// func TestHandler_reduce(t *testing.T) { +// type fields struct { +// projection Projection +// } +// type args struct { +// event eventstore.Event +// } +// tests := []struct { +// name string +// fields fields +// args args +// isErr func(t *testing.T, err error) +// shouldBeCalled bool +// }{ +// { +// name: "", +// fields: fields{ +// projection: &projection{ +// reducers: []AggregateReducer{ +// { +// Aggregate: "aggregate", +// EventRedusers: []EventReducer{ +// { +// Event: "event", +// Reduce: (&mockEventReducer{ +// statement: new(Statement), +// }).reduce, +// }, +// }, +// }, +// }, +// }, +// }, +// }, +// } +// for _, tt := range tests { +// if tt.isErr == nil { +// tt.isErr = func(t *testing.T, err error) { +// if err != nil { +// t.Error("expected no error got:", err) +// } +// } +// } +// t.Run(tt.name, func(t *testing.T) { +// h := &Handler{ +// projection: tt.fields.projection, +// } +// got, err := h.reduce(tt.args.event) +// tt.isErr(t, err) +// if tt.shouldBeCalled != tt. +// if !reflect.DeepEqual(got, tt.want) { +// t.Errorf("Handler.reduce() = %v, want %v", got, tt.want) +// } +// }) +// } +// } diff --git a/internal/eventstore/local_crdb_test.go b/internal/eventstore/local_crdb_test.go index 85dc78aea..81ca93728 100644 --- a/internal/eventstore/local_crdb_test.go +++ b/internal/eventstore/local_crdb_test.go @@ -1,7 +1,9 @@ package eventstore_test import ( + "context" "database/sql" + "encoding/json" "os" "testing" "time" @@ -12,10 +14,16 @@ import ( "github.com/zitadel/zitadel/cmd/initialise" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/database/cockroach" + "github.com/zitadel/zitadel/internal/eventstore" + es_sql "github.com/zitadel/zitadel/internal/eventstore/repository/sql" + new_es "github.com/zitadel/zitadel/internal/eventstore/v3" ) var ( testCRDBClient *database.DB + queriers map[string]eventstore.Querier = make(map[string]eventstore.Querier) + pushers map[string]eventstore.Pusher = make(map[string]eventstore.Pusher) + clients map[string]*database.DB = make(map[string]*database.DB) ) func TestMain(m *testing.M) { @@ -27,31 +35,44 @@ func TestMain(m *testing.M) { testCRDBClient = &database.DB{ Database: new(testDB), } - testCRDBClient.DB, err = sql.Open("postgres", ts.PGURL().String()) if err != nil { logging.WithFields("error", err).Fatal("unable to connect to db") } - if err != nil { - logging.WithFields("error", err).Fatal("unable to connect to db") - } if err = testCRDBClient.Ping(); err != nil { logging.WithFields("error", err).Fatal("unable to ping db") } + v2 := &es_sql.CRDB{DB: testCRDBClient} + queriers["v2(inmemory)"] = v2 + clients["v2(inmemory)"] = testCRDBClient + + pushers["v3(inmemory)"] = new_es.NewEventstore(testCRDBClient) + clients["v3(inmemory)"] = testCRDBClient + + if localDB, err := connectLocalhost(); err == nil { + if err = initDB(localDB); err != nil { + logging.WithFields("error", err).Fatal("migrations failed") + } + pushers["v3(singlenode)"] = new_es.NewEventstore(localDB) + clients["v3(singlenode)"] = localDB + } + + // pushers["v2(inmemory)"] = v2 + defer func() { testCRDBClient.Close() ts.Stop() }() - if err = initDB(testCRDBClient.DB); err != nil { + if err = initDB(testCRDBClient); err != nil { logging.WithFields("error", err).Fatal("migrations failed") } os.Exit(m.Run()) } -func initDB(db *sql.DB) error { +func initDB(db *database.DB) error { initialise.ReadStmts("cockroach") config := new(database.Config) config.SetConnector(&cockroach.Config{ @@ -67,7 +88,28 @@ func initDB(db *sql.DB) error { if err != nil { return err } - return initialise.VerifyZitadel(db, *config) + err = initialise.VerifyZitadel(db, *config) + if err != nil { + return err + } + // create old events + _, err = db.Exec(oldEventsTable) + return err +} + +func connectLocalhost() (*database.DB, error) { + client, err := sql.Open("pgx", "postgresql://root@localhost:26257/defaultdb?sslmode=disable") + if err != nil { + return nil, err + } + if err = client.Ping(); err != nil { + return nil, err + } + + return &database.DB{ + DB: client, + Database: new(testDB), + }, nil } type testDB struct{} @@ -78,4 +120,122 @@ func (*testDB) DatabaseName() string { return "db" } func (*testDB) Username() string { return "user" } -func (*testDB) Type() string { return "type" } +func (*testDB) Type() string { return "cockroach" } + +func generateCommand(aggregateType eventstore.AggregateType, aggregateID string, opts ...func(*testEvent)) eventstore.Command { + e := &testEvent{ + BaseEvent: eventstore.BaseEvent{ + Agg: &eventstore.Aggregate{ + ID: aggregateID, + Type: aggregateType, + ResourceOwner: "ro", + Version: "v1", + }, + Service: "svc", + EventType: "test.created", + }, + } + + for _, opt := range opts { + opt(e) + } + + return e +} + +type testEvent struct { + eventstore.BaseEvent + uniqueConstraints []*eventstore.UniqueConstraint +} + +func (e *testEvent) Payload() any { + return e.BaseEvent.Data +} + +func (e *testEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return e.uniqueConstraints +} + +func canceledCtx() context.Context { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + return ctx +} + +func fillUniqueData(unique_type, field, instanceID string) error { + _, err := testCRDBClient.Exec("INSERT INTO eventstore.unique_constraints (unique_type, unique_field, instance_id) VALUES ($1, $2, $3)", unique_type, field, instanceID) + return err +} + +func generateAddUniqueConstraint(table, uniqueField string) func(e *testEvent) { + return func(e *testEvent) { + e.uniqueConstraints = append(e.uniqueConstraints, + &eventstore.UniqueConstraint{ + UniqueType: table, + UniqueField: uniqueField, + Action: eventstore.UniqueConstraintAdd, + }, + ) + } +} + +func generateRemoveUniqueConstraint(table, uniqueField string) func(e *testEvent) { + return func(e *testEvent) { + e.uniqueConstraints = append(e.uniqueConstraints, + &eventstore.UniqueConstraint{ + UniqueType: table, + UniqueField: uniqueField, + Action: eventstore.UniqueConstraintRemove, + }, + ) + } +} + +func withTestData(data any) func(e *testEvent) { + return func(e *testEvent) { + d, err := json.Marshal(data) + if err != nil { + panic("marshal data failed") + } + e.BaseEvent.Data = d + } +} + +func cleanupEventstore(client *database.DB) func() { + return func() { + _, err := client.Exec("TRUNCATE eventstore.events") + if err != nil { + logging.Warnf("unable to truncate events: %v", err) + } + _, err = client.Exec("TRUNCATE eventstore.events2") + if err != nil { + logging.Warnf("unable to truncate events: %v", err) + } + _, err = client.Exec("TRUNCATE eventstore.unique_constraints") + if err != nil { + logging.Warnf("unable to truncate unique constraints: %v", err) + } + } +} + +const oldEventsTable = `CREATE TABLE IF NOT EXISTS eventstore.events ( + id UUID DEFAULT gen_random_uuid() + , event_type TEXT NOT NULL + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + , aggregate_version TEXT NOT NULL + , event_sequence BIGINT NOT NULL + , previous_aggregate_sequence BIGINT + , previous_aggregate_type_sequence INT8 + , creation_date TIMESTAMPTZ NOT NULL DEFAULT now() + , created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp() + , event_data JSONB + , editor_user TEXT NOT NULL + , editor_service TEXT + , resource_owner TEXT NOT NULL + , instance_id TEXT NOT NULL + , "position" DECIMAL NOT NULL + , in_tx_order INTEGER NOT NULL + + , PRIMARY KEY (instance_id, aggregate_type, aggregate_id, event_sequence DESC) +);` diff --git a/internal/eventstore/read_model.go b/internal/eventstore/read_model.go index 3e07743fa..6bf7dcf4f 100644 --- a/internal/eventstore/read_model.go +++ b/internal/eventstore/read_model.go @@ -39,9 +39,9 @@ func (rm *ReadModel) Reduce() error { } if rm.CreationDate.IsZero() { - rm.CreationDate = rm.Events[0].CreationDate() + rm.CreationDate = rm.Events[0].CreatedAt() } - rm.ChangeDate = rm.Events[len(rm.Events)-1].CreationDate() + rm.ChangeDate = rm.Events[len(rm.Events)-1].CreatedAt() rm.ProcessedSequence = rm.Events[len(rm.Events)-1].Sequence() // all events processed and not needed anymore rm.Events = nil diff --git a/internal/eventstore/repository/event.go b/internal/eventstore/repository/event.go index 8583c6d3f..d55661f15 100644 --- a/internal/eventstore/repository/event.go +++ b/internal/eventstore/repository/event.go @@ -2,24 +2,23 @@ package repository import ( "database/sql" + "encoding/json" "time" + + "github.com/zitadel/zitadel/internal/eventstore" ) -//Event represents all information about a manipulation of an aggregate +var _ eventstore.Event = (*Event)(nil) + +// Event represents all information about a manipulation of an aggregate type Event struct { //ID is a generated uuid for this event ID string - //Sequence is the sequence of the event - Sequence uint64 - - //PreviousAggregateSequence is the sequence of the previous sequence of the aggregate (e.g. org.250989) - // if it's 0 then it's the first event of this aggregate - PreviousAggregateSequence uint64 - - //PreviousAggregateTypeSequence is the sequence of the previous sequence of the aggregate root (e.g. org) - // the first event of the aggregate has previous aggregate root sequence 0 - PreviousAggregateTypeSequence uint64 + // Seq is the sequence of the event + Seq uint64 + // Pos is the global sequence of the event multiple events can have the same sequence + Pos float64 //CreationDate is the time the event is created // it's used for human readability. @@ -27,17 +26,14 @@ type Event struct { // time drifts in different services could cause integrity problems CreationDate time.Time - //Type describes the cause of the event (e.g. user.added) + // Typ describes the cause of the event (e.g. user.added) // it should always be in past-form - Type EventType + Typ eventstore.EventType //Data describe the changed fields (e.g. userName = "hodor") // data must always a pointer to a struct, a struct or a byte array containing json bytes Data []byte - //EditorService should be a unique identifier for the service which created the event - // it's meant for maintainability - EditorService string //EditorUser should be a unique identifier for the user which created the event // it's meant for maintainability. // It's recommend to use the aggregate id of the user @@ -45,13 +41,13 @@ type Event struct { //Version describes the definition of the aggregate at a certain point in time // it's used in read models to reduce the events in the correct definition - Version Version + Version eventstore.Version //AggregateID id is the unique identifier of the aggregate // the client must generate it by it's own AggregateID string //AggregateType describes the meaning of the aggregate for this event // it could an object like user - AggregateType AggregateType + AggregateType eventstore.AggregateType //ResourceOwner is the organisation which owns this aggregate // an aggregate can only be managed by one organisation // use the ID of the org @@ -59,10 +55,68 @@ type Event struct { //InstanceID is the instance where this event belongs to // use the ID of the instance InstanceID string + + Constraints []*eventstore.UniqueConstraint } -//EventType is the description of the change -type EventType string +// Aggregate implements [eventstore.Event] +func (e *Event) Aggregate() *eventstore.Aggregate { + return &eventstore.Aggregate{ + ID: e.AggregateID, + Type: e.AggregateType, + ResourceOwner: e.ResourceOwner.String, + InstanceID: e.InstanceID, + Version: e.Version, + } +} -//AggregateType is the object name -type AggregateType string +// Creator implements [eventstore.Event] +func (e *Event) Creator() string { + return e.EditorUser +} + +// Type implements [eventstore.Event] +func (e *Event) Type() eventstore.EventType { + return e.Typ +} + +// Revision implements [eventstore.Event] +func (e *Event) Revision() uint16 { + return 0 +} + +// Sequence implements [eventstore.Event] +func (e *Event) Sequence() uint64 { + return e.Seq +} + +// Position implements [eventstore.Event] +func (e *Event) Position() float64 { + return e.Pos +} + +// CreatedAt implements [eventstore.Event] +func (e *Event) CreatedAt() time.Time { + return e.CreationDate +} + +// Unmarshal implements [eventstore.Event] +func (e *Event) Unmarshal(ptr any) error { + if len(e.Data) == 0 { + return nil + } + return json.Unmarshal(e.Data, ptr) +} + +// DataAsBytes implements [eventstore.Event] +func (e *Event) DataAsBytes() []byte { + return e.Data +} + +func (e *Event) Payload() any { + return e.Data +} + +func (e *Event) UniqueConstraints() []*eventstore.UniqueConstraint { + return e.Constraints +} diff --git a/internal/eventstore/repository/mock/gen_mock.go b/internal/eventstore/repository/mock/gen_mock.go index 73714228c..1895e26c6 100644 --- a/internal/eventstore/repository/mock/gen_mock.go +++ b/internal/eventstore/repository/mock/gen_mock.go @@ -1,3 +1,3 @@ package mock -//go:generate mockgen -package mock -destination ./repository.mock.go github.com/zitadel/zitadel/internal/eventstore/repository Repository +//go:generate mockgen -package mock -destination ./repository.mock.go github.com/zitadel/zitadel/internal/eventstore Querier,Pusher diff --git a/internal/eventstore/repository/mock/repository.mock.go b/internal/eventstore/repository/mock/repository.mock.go index 663e3a49f..f2b4fb089 100644 --- a/internal/eventstore/repository/mock/repository.mock.go +++ b/internal/eventstore/repository/mock/repository.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/zitadel/zitadel/internal/eventstore/repository (interfaces: Repository) +// Source: github.com/zitadel/zitadel/internal/eventstore (interfaces: Querier,Pusher) // Package mock is a generated GoMock package. package mock @@ -9,63 +9,49 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - repository "github.com/zitadel/zitadel/internal/eventstore/repository" + eventstore "github.com/zitadel/zitadel/internal/eventstore" ) -// MockRepository is a mock of Repository interface. -type MockRepository struct { +// MockQuerier is a mock of Querier interface. +type MockQuerier struct { ctrl *gomock.Controller - recorder *MockRepositoryMockRecorder + recorder *MockQuerierMockRecorder } -// MockRepositoryMockRecorder is the mock recorder for MockRepository. -type MockRepositoryMockRecorder struct { - mock *MockRepository +// MockQuerierMockRecorder is the mock recorder for MockQuerier. +type MockQuerierMockRecorder struct { + mock *MockQuerier } -// NewMockRepository creates a new mock instance. -func NewMockRepository(ctrl *gomock.Controller) *MockRepository { - mock := &MockRepository{ctrl: ctrl} - mock.recorder = &MockRepositoryMockRecorder{mock} +// NewMockQuerier creates a new mock instance. +func NewMockQuerier(ctrl *gomock.Controller) *MockQuerier { + mock := &MockQuerier{ctrl: ctrl} + mock.recorder = &MockQuerierMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { +func (m *MockQuerier) EXPECT() *MockQuerierMockRecorder { return m.recorder } -// CreateInstance mocks base method. -func (m *MockRepository) CreateInstance(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateInstance", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateInstance indicates an expected call of CreateInstance. -func (mr *MockRepositoryMockRecorder) CreateInstance(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInstance", reflect.TypeOf((*MockRepository)(nil).CreateInstance), arg0, arg1) -} - // Filter mocks base method. -func (m *MockRepository) Filter(arg0 context.Context, arg1 *repository.SearchQuery) ([]*repository.Event, error) { +func (m *MockQuerier) Filter(arg0 context.Context, arg1 *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Filter", arg0, arg1) - ret0, _ := ret[0].([]*repository.Event) + ret0, _ := ret[0].([]eventstore.Event) ret1, _ := ret[1].(error) return ret0, ret1 } // Filter indicates an expected call of Filter. -func (mr *MockRepositoryMockRecorder) Filter(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockQuerierMockRecorder) Filter(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockRepository)(nil).Filter), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockQuerier)(nil).Filter), arg0, arg1) } // Health mocks base method. -func (m *MockRepository) Health(arg0 context.Context) error { +func (m *MockQuerier) Health(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(error) @@ -73,13 +59,13 @@ func (m *MockRepository) Health(arg0 context.Context) error { } // Health indicates an expected call of Health. -func (mr *MockRepositoryMockRecorder) Health(arg0 interface{}) *gomock.Call { +func (mr *MockQuerierMockRecorder) Health(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockRepository)(nil).Health), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockQuerier)(nil).Health), arg0) } // InstanceIDs mocks base method. -func (m *MockRepository) InstanceIDs(arg0 context.Context, arg1 *repository.SearchQuery) ([]string, error) { +func (m *MockQuerier) InstanceIDs(arg0 context.Context, arg1 *eventstore.SearchQueryBuilder) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InstanceIDs", arg0, arg1) ret0, _ := ret[0].([]string) @@ -88,41 +74,79 @@ func (m *MockRepository) InstanceIDs(arg0 context.Context, arg1 *repository.Sear } // InstanceIDs indicates an expected call of InstanceIDs. -func (mr *MockRepositoryMockRecorder) InstanceIDs(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockQuerierMockRecorder) InstanceIDs(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDs", reflect.TypeOf((*MockRepository)(nil).InstanceIDs), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceIDs", reflect.TypeOf((*MockQuerier)(nil).InstanceIDs), arg0, arg1) } // LatestSequence mocks base method. -func (m *MockRepository) LatestSequence(arg0 context.Context, arg1 *repository.SearchQuery) (uint64, error) { +func (m *MockQuerier) LatestSequence(arg0 context.Context, arg1 *eventstore.SearchQueryBuilder) (float64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LatestSequence", arg0, arg1) - ret0, _ := ret[0].(uint64) + ret0, _ := ret[0].(float64) ret1, _ := ret[1].(error) return ret0, ret1 } // LatestSequence indicates an expected call of LatestSequence. -func (mr *MockRepositoryMockRecorder) LatestSequence(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockQuerierMockRecorder) LatestSequence(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSequence", reflect.TypeOf((*MockRepository)(nil).LatestSequence), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSequence", reflect.TypeOf((*MockQuerier)(nil).LatestSequence), arg0, arg1) } -// Push mocks base method. -func (m *MockRepository) Push(arg0 context.Context, arg1 []*repository.Event, arg2 ...*repository.UniqueConstraint) error { +// MockPusher is a mock of Pusher interface. +type MockPusher struct { + ctrl *gomock.Controller + recorder *MockPusherMockRecorder +} + +// MockPusherMockRecorder is the mock recorder for MockPusher. +type MockPusherMockRecorder struct { + mock *MockPusher +} + +// NewMockPusher creates a new mock instance. +func NewMockPusher(ctrl *gomock.Controller) *MockPusher { + mock := &MockPusher{ctrl: ctrl} + mock.recorder = &MockPusherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPusher) EXPECT() *MockPusherMockRecorder { + return m.recorder +} + +// Health mocks base method. +func (m *MockPusher) Health(arg0 context.Context) error { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Push", varargs...) + ret := m.ctrl.Call(m, "Health", arg0) ret0, _ := ret[0].(error) return ret0 } -// Push indicates an expected call of Push. -func (mr *MockRepositoryMockRecorder) Push(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +// Health indicates an expected call of Health. +func (mr *MockPusherMockRecorder) Health(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockRepository)(nil).Push), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockPusher)(nil).Health), arg0) +} + +// Push mocks base method. +func (m *MockPusher) Push(arg0 context.Context, arg1 ...eventstore.Command) ([]eventstore.Event, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Push", varargs...) + ret0, _ := ret[0].([]eventstore.Event) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Push indicates an expected call of Push. +func (mr *MockPusherMockRecorder) Push(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockPusher)(nil).Push), varargs...) } diff --git a/internal/eventstore/repository/mock/repository.mock.impl.go b/internal/eventstore/repository/mock/repository.mock.impl.go index e21f4fd7c..b6d5eabbd 100644 --- a/internal/eventstore/repository/mock/repository.mock.impl.go +++ b/internal/eventstore/repository/mock/repository.mock.impl.go @@ -2,92 +2,208 @@ package mock import ( "context" + "encoding/json" + "fmt" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) +type MockRepository struct { + *MockPusher + *MockQuerier +} + func NewRepo(t *testing.T) *MockRepository { - return NewMockRepository(gomock.NewController(t)) + controller := gomock.NewController(t) + return &MockRepository{ + MockPusher: NewMockPusher(controller), + MockQuerier: NewMockQuerier(controller), + } } func (m *MockRepository) ExpectFilterNoEventsNoError() *MockRepository { - m.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(nil, nil) + m.MockQuerier.ctrl.T.Helper() + + m.MockQuerier.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(nil, nil) return m } -func (m *MockRepository) ExpectFilterEvents(events ...*repository.Event) *MockRepository { - m.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(events, nil) +func (m *MockRepository) ExpectFilterEvents(events ...eventstore.Event) *MockRepository { + m.MockQuerier.ctrl.T.Helper() + + m.MockQuerier.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(events, nil) return m } func (m *MockRepository) ExpectFilterEventsError(err error) *MockRepository { - m.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(nil, err) + m.MockQuerier.ctrl.T.Helper() + + m.MockQuerier.EXPECT().Filter(gomock.Any(), gomock.Any()).Return(nil, err) return m } func (m *MockRepository) ExpectInstanceIDs(hasFilters []*repository.Filter, instanceIDs ...string) *MockRepository { + m.MockQuerier.ctrl.T.Helper() + matcher := gomock.Any() if len(hasFilters) > 0 { - matcher = &filterQueryMatcher{Filters: [][]*repository.Filter{hasFilters}} + matcher = &filterQueryMatcher{SubQueries: [][]*repository.Filter{hasFilters}} } - m.EXPECT().InstanceIDs(gomock.Any(), matcher).Return(instanceIDs, nil) + m.MockQuerier.EXPECT().InstanceIDs(gomock.Any(), matcher).Return(instanceIDs, nil) return m } func (m *MockRepository) ExpectInstanceIDsError(err error) *MockRepository { - m.EXPECT().InstanceIDs(gomock.Any(), gomock.Any()).Return(nil, err) + m.MockQuerier.ctrl.T.Helper() + + m.MockQuerier.EXPECT().InstanceIDs(gomock.Any(), gomock.Any()).Return(nil, err) return m } -func (m *MockRepository) ExpectPush(expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository { - m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - assert.Equal(m.ctrl.T, expectedEvents, events) - if expectedUniqueConstraints == nil { - expectedUniqueConstraints = []*repository.UniqueConstraint{} +func (m *MockRepository) ExpectPush(expectedCommands []eventstore.Command) *MockRepository { + m.MockPusher.EXPECT().Push(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, commands ...eventstore.Command) ([]eventstore.Event, error) { + m.MockPusher.ctrl.T.Helper() + + if len(expectedCommands) != len(commands) { + return nil, fmt.Errorf("unexpected amount of commands: want %d, got %d", len(expectedCommands), len(commands)) } - assert.Equal(m.ctrl.T, expectedUniqueConstraints, uniqueConstraints) - return nil - }, - ) - return m -} + for i, expectedCommand := range expectedCommands { + if !assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Aggregate(), commands[i].Aggregate()) { + m.MockPusher.ctrl.T.Errorf("invalid command.Aggregate [%d]: expected: %#v got: %#v", i, expectedCommand.Aggregate(), commands[i].Aggregate()) + } + if !assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Creator(), commands[i].Creator()) { + m.MockPusher.ctrl.T.Errorf("invalid command.Creator [%d]: expected: %#v got: %#v", i, expectedCommand.Creator(), commands[i].Creator()) + } + if !assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Type(), commands[i].Type()) { + m.MockPusher.ctrl.T.Errorf("invalid command.Type [%d]: expected: %#v got: %#v", i, expectedCommand.Type(), commands[i].Type()) + } + if !assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Revision(), commands[i].Revision()) { + m.MockPusher.ctrl.T.Errorf("invalid command.Revision [%d]: expected: %#v got: %#v", i, expectedCommand.Revision(), commands[i].Revision()) + } -func (m *MockRepository) ExpectPushFailed(err error, expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository { - m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - assert.Equal(m.ctrl.T, expectedEvents, events) - if expectedUniqueConstraints == nil { - expectedUniqueConstraints = []*repository.UniqueConstraint{} + var expectedPayload []byte + expectedPayload, ok := expectedCommand.Payload().([]byte) + if !ok { + expectedPayload, _ = json.Marshal(expectedCommand.Payload()) + } + if string(expectedPayload) == "" { + expectedPayload = []byte("null") + } + gotPayload, _ := json.Marshal(commands[i].Payload()) + + if !assert.Equal(m.MockPusher.ctrl.T, expectedPayload, gotPayload) { + m.MockPusher.ctrl.T.Errorf("invalid command.Payload [%d]: expected: %#v got: %#v", i, expectedCommand.Payload(), commands[i].Payload()) + } + if !assert.ElementsMatch(m.MockPusher.ctrl.T, expectedCommand.UniqueConstraints(), commands[i].UniqueConstraints()) { + m.MockPusher.ctrl.T.Errorf("invalid command.UniqueConstraints [%d]: expected: %#v got: %#v", i, expectedCommand.UniqueConstraints(), commands[i].UniqueConstraints()) + } } - assert.Equal(m.ctrl.T, expectedUniqueConstraints, uniqueConstraints) - return err + events := make([]eventstore.Event, len(commands)) + for i, command := range commands { + events[i] = &mockEvent{ + Command: command, + } + } + return events, nil }, ) return m } -func (m *MockRepository) ExpectRandomPush(expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository { - m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - assert.Len(m.ctrl.T, events, len(expectedEvents)) - assert.Len(m.ctrl.T, expectedUniqueConstraints, len(uniqueConstraints)) - return nil +func (m *MockRepository) ExpectPushFailed(err error, expectedCommands []eventstore.Command) *MockRepository { + m.MockPusher.ctrl.T.Helper() + + m.MockPusher.EXPECT().Push(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, commands ...eventstore.Command) ([]eventstore.Event, error) { + if len(expectedCommands) != len(commands) { + return nil, fmt.Errorf("unexpected amount of commands: want %d, got %d", len(expectedCommands), len(commands)) + } + for i, expectedCommand := range expectedCommands { + assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Aggregate(), commands[i].Aggregate()) + assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Creator(), commands[i].Creator()) + assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Type(), commands[i].Type()) + assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Revision(), commands[i].Revision()) + assert.Equal(m.MockPusher.ctrl.T, expectedCommand.Payload(), commands[i].Payload()) + assert.ElementsMatch(m.MockPusher.ctrl.T, expectedCommand.UniqueConstraints(), commands[i].UniqueConstraints()) + } + + return nil, err }, ) return m } -func (m *MockRepository) ExpectRandomPushFailed(err error, expectedEvents []*repository.Event, expectedUniqueConstraints ...*repository.UniqueConstraint) *MockRepository { - m.EXPECT().Push(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - assert.Len(m.ctrl.T, events, len(expectedEvents)) - assert.Len(m.ctrl.T, expectedUniqueConstraints, len(uniqueConstraints)) - return err +type mockEvent struct { + eventstore.Command + sequence uint64 + createdAt time.Time +} + +// DataAsBytes implements eventstore.Event +func (e *mockEvent) DataAsBytes() []byte { + if e.Payload() == nil { + return nil + } + payload, err := json.Marshal(e.Payload()) + if err != nil { + panic("unable to unmarshal") + } + return payload +} + +func (e *mockEvent) Unmarshal(ptr any) error { + if e.Payload() == nil { + return nil + } + payload, err := json.Marshal(e.Payload()) + if err != nil { + return err + } + return json.Unmarshal(payload, ptr) +} + +func (e *mockEvent) Sequence() uint64 { + return e.sequence +} + +func (e *mockEvent) Position() float64 { + return 0 +} + +func (e *mockEvent) CreatedAt() time.Time { + return e.createdAt +} + +func (m *MockRepository) ExpectRandomPush(expectedCommands []eventstore.Command) *MockRepository { + m.MockPusher.EXPECT().Push(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, commands ...eventstore.Command) ([]eventstore.Event, error) { + assert.Len(m.MockPusher.ctrl.T, commands, len(expectedCommands)) + + events := make([]eventstore.Event, len(commands)) + for i, command := range commands { + events[i] = &mockEvent{ + Command: command, + } + } + + return events, nil + }, + ) + return m +} + +func (m *MockRepository) ExpectRandomPushFailed(err error, expectedEvents []eventstore.Command) *MockRepository { + m.MockPusher.EXPECT().Push(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, events ...eventstore.Command) ([]eventstore.Event, error) { + assert.Len(m.MockPusher.ctrl.T, events, len(expectedEvents)) + return nil, err }, ) return m diff --git a/internal/eventstore/repository/mock/repository.mock.matcher.searchquery.go b/internal/eventstore/repository/mock/repository.mock.matcher.searchquery.go index 7d8237075..f320161d8 100644 --- a/internal/eventstore/repository/mock/repository.mock.matcher.searchquery.go +++ b/internal/eventstore/repository/mock/repository.mock.matcher.searchquery.go @@ -11,7 +11,7 @@ type filterQueryMatcher repository.SearchQuery func (f *filterQueryMatcher) String() string { var filterLists []string - for _, filterSlice := range f.Filters { + for _, filterSlice := range f.SubQueries { var str string for _, filter := range filterSlice { str += "," + (*filterMatcher)(filter).String() @@ -24,15 +24,15 @@ func (f *filterQueryMatcher) String() string { func (f *filterQueryMatcher) Matches(x interface{}) bool { other := x.(*repository.SearchQuery) - if len(f.Filters) != len(other.Filters) { + if len(f.SubQueries) != len(other.SubQueries) { return false } - for filterSliceIdx, filterSlice := range f.Filters { - if len(filterSlice) != len(other.Filters[filterSliceIdx]) { + for filterSliceIdx, filterSlice := range f.SubQueries { + if len(filterSlice) != len(other.SubQueries[filterSliceIdx]) { return false } - for filterIdx, filter := range f.Filters[filterSliceIdx] { - if !(*filterMatcher)(filter).Matches(other.Filters[filterSliceIdx][filterIdx]) { + for filterIdx, filter := range f.SubQueries[filterSliceIdx] { + if !(*filterMatcher)(filter).Matches(other.SubQueries[filterSliceIdx][filterIdx]) { return false } } diff --git a/internal/eventstore/repository/repository.go b/internal/eventstore/repository/repository.go deleted file mode 100644 index 964b51525..000000000 --- a/internal/eventstore/repository/repository.go +++ /dev/null @@ -1,23 +0,0 @@ -package repository - -import ( - "context" -) - -//Repository pushes and filters events -type Repository interface { - //Health checks if the connection to the storage is available - Health(ctx context.Context) error - // Push adds all events of the given aggregates to the event streams of the aggregates. - // if unique constraints are pushed, they will be added to the unique table for checking unique constraint violations - // This call is transaction save. The transaction will be rolled back if one event fails - Push(ctx context.Context, events []*Event, uniqueConstraints ...*UniqueConstraint) error - // Filter returns all events matching the given search query - Filter(ctx context.Context, searchQuery *SearchQuery) (events []*Event, err error) - //LatestSequence returns the latest sequence found by the search query - LatestSequence(ctx context.Context, queryFactory *SearchQuery) (uint64, error) - //InstanceIDs returns the instance ids found by the search query - InstanceIDs(ctx context.Context, queryFactory *SearchQuery) ([]string, error) - //CreateInstance creates a new sequence for the given instance - CreateInstance(ctx context.Context, instanceID string) error -} diff --git a/internal/eventstore/repository/search_query.go b/internal/eventstore/repository/search_query.go index 2b315289c..7c2ff31eb 100644 --- a/internal/eventstore/repository/search_query.go +++ b/internal/eventstore/repository/search_query.go @@ -3,38 +3,29 @@ package repository import ( "database/sql" + "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" ) // SearchQuery defines the which and how data are queried type SearchQuery struct { - Columns Columns - Limit uint64 - Desc bool - Filters [][]*Filter - Tx *sql.Tx - AllowTimeTravel bool -} + Columns eventstore.Columns -// Columns defines which fields of the event are needed for the query -type Columns int32 + SubQueries [][]*Filter + Tx *sql.Tx + AllowTimeTravel bool + AwaitOpenTransactions bool + Limit uint64 + Desc bool -const ( - //ColumnsEvent represents all fields of an event - ColumnsEvent = iota + 1 - //ColumnsMaxSequence represents the latest sequence of the filtered events - ColumnsMaxSequence - // ColumnsInstanceIDs represents the instance ids of the filtered events - ColumnsInstanceIDs - - columnsCount -) - -func (c Columns) Validate() error { - if c <= 0 || c >= columnsCount { - return errors.ThrowPreconditionFailed(nil, "REPOS-x8R35", "column out of range") - } - return nil + InstanceID *Filter + ExcludedInstances *Filter + Creator *Filter + Owner *Filter + Position *Filter + Sequence *Filter + CreatedAt *Filter } // Filter represents all fields needed to compare a field of an event with a value @@ -88,6 +79,8 @@ const ( FieldEventData //FieldCreationDate represents the creation date field FieldCreationDate + // FieldPosition represents the field of the global sequence + FieldPosition fieldCount ) @@ -117,3 +110,164 @@ func (f *Filter) Validate() error { } return nil } + +func QueryFromBuilder(builder *eventstore.SearchQueryBuilder) (*SearchQuery, error) { + if builder == nil || + builder.GetColumns().Validate() != nil { + return nil, errors.ThrowPreconditionFailed(nil, "MODEL-4m9gs", "builder invalid") + } + + query := &SearchQuery{ + Columns: builder.GetColumns(), + Limit: builder.GetLimit(), + Desc: builder.GetDesc(), + Tx: builder.GetTx(), + AllowTimeTravel: builder.GetAllowTimeTravel(), + AwaitOpenTransactions: builder.GetAwaitOpenTransactions(), + // Queries: make([]*Filter, 0, 7), + SubQueries: make([][]*Filter, len(builder.GetQueries())), + } + + for _, f := range []func(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter{ + instanceIDFilter, + excludedInstanceIDFilter, + editorUserFilter, + resourceOwnerFilter, + positionAfterFilter, + eventSequenceGreaterFilter, + creationDateAfterFilter, + } { + filter := f(builder, query) + if filter == nil { + continue + } + if err := filter.Validate(); err != nil { + return nil, err + } + } + + for i, q := range builder.GetQueries() { + for _, f := range []func(query *eventstore.SearchQuery) *Filter{ + aggregateTypeFilter, + aggregateIDFilter, + eventTypeFilter, + eventDataFilter, + } { + filter := f(q) + if filter == nil { + continue + } + if err := filter.Validate(); err != nil { + return nil, err + } + query.SubQueries[i] = append(query.SubQueries[i], filter) + } + } + + return query, nil +} + +func eventSequenceGreaterFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetEventSequenceGreater() == 0 { + return nil + } + sortOrder := OperationGreater + if builder.GetDesc() { + sortOrder = OperationLess + } + query.Sequence = NewFilter(FieldSequence, builder.GetEventSequenceGreater(), sortOrder) + return query.Sequence +} + +func excludedInstanceIDFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if len(builder.GetExcludedInstanceIDs()) == 0 { + return nil + } + query.ExcludedInstances = NewFilter(FieldInstanceID, database.TextArray[string](builder.GetExcludedInstanceIDs()), OperationNotIn) + return query.ExcludedInstances +} + +func creationDateAfterFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetCreationDateAfter().IsZero() { + return nil + } + query.CreatedAt = NewFilter(FieldCreationDate, builder.GetCreationDateAfter(), OperationGreater) + return query.CreatedAt +} + +func resourceOwnerFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetResourceOwner() == "" { + return nil + } + query.Owner = NewFilter(FieldResourceOwner, builder.GetResourceOwner(), OperationEquals) + return query.Owner +} + +func editorUserFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetEditorUser() == "" { + return nil + } + query.Creator = NewFilter(FieldEditorUser, builder.GetEditorUser(), OperationEquals) + return query.Creator +} + +func instanceIDFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetInstanceID() == nil { + return nil + } + query.InstanceID = NewFilter(FieldInstanceID, *builder.GetInstanceID(), OperationEquals) + return query.InstanceID +} + +func positionAfterFilter(builder *eventstore.SearchQueryBuilder, query *SearchQuery) *Filter { + if builder.GetPositionAfter() == 0 { + return nil + } + query.Position = NewFilter(FieldPosition, builder.GetPositionAfter(), OperationGreater) + return query.Position +} + +func aggregateIDFilter(query *eventstore.SearchQuery) *Filter { + if len(query.GetAggregateIDs()) < 1 { + return nil + } + if len(query.GetAggregateIDs()) == 1 { + return NewFilter(FieldAggregateID, query.GetAggregateIDs()[0], OperationEquals) + } + return NewFilter(FieldAggregateID, database.TextArray[string](query.GetAggregateIDs()), OperationIn) +} + +func eventTypeFilter(query *eventstore.SearchQuery) *Filter { + if len(query.GetEventTypes()) < 1 { + return nil + } + if len(query.GetEventTypes()) == 1 { + return NewFilter(FieldEventType, query.GetEventTypes()[0], OperationEquals) + } + eventTypes := make(database.TextArray[eventstore.EventType], len(query.GetEventTypes())) + for i, eventType := range query.GetEventTypes() { + eventTypes[i] = eventType + } + return NewFilter(FieldEventType, eventTypes, OperationIn) +} + +func aggregateTypeFilter(query *eventstore.SearchQuery) *Filter { + if len(query.GetAggregateTypes()) < 1 { + return nil + } + if len(query.GetAggregateTypes()) == 1 { + return NewFilter(FieldAggregateType, query.GetAggregateTypes()[0], OperationEquals) + } + aggregateTypes := make(database.TextArray[eventstore.AggregateType], len(query.GetAggregateTypes())) + for i, aggregateType := range query.GetAggregateTypes() { + aggregateTypes[i] = aggregateType + } + return NewFilter(FieldAggregateType, aggregateTypes, OperationIn) +} + +func eventDataFilter(query *eventstore.SearchQuery) *Filter { + if len(query.GetEventData()) == 0 { + return nil + } + return NewFilter(FieldEventData, query.GetEventData(), OperationJSONContains) +} diff --git a/internal/eventstore/repository/search_query_test.go b/internal/eventstore/repository/search_query_test.go index d72235b34..a830155e3 100644 --- a/internal/eventstore/repository/search_query_test.go +++ b/internal/eventstore/repository/search_query_test.go @@ -3,6 +3,8 @@ package repository import ( "reflect" "testing" + + "github.com/zitadel/zitadel/internal/eventstore" ) func TestNewFilter(t *testing.T) { @@ -105,7 +107,7 @@ func TestFilter_Validate(t *testing.T) { func TestColumns_Validate(t *testing.T) { type fields struct { - columns Columns + columns eventstore.Columns } tests := []struct { name string @@ -115,7 +117,7 @@ func TestColumns_Validate(t *testing.T) { { name: "correct filter", fields: fields{ - columns: ColumnsEvent, + columns: eventstore.ColumnsEvent, }, wantErr: false, }, @@ -129,7 +131,7 @@ func TestColumns_Validate(t *testing.T) { { name: "columns too high", fields: fields{ - columns: columnsCount, + columns: 100, }, wantErr: true, }, diff --git a/internal/eventstore/repository/sql/crdb.go b/internal/eventstore/repository/sql/crdb.go index 14fab23b6..6d0cda612 100644 --- a/internal/eventstore/repository/sql/crdb.go +++ b/internal/eventstore/repository/sql/crdb.go @@ -3,6 +3,7 @@ package sql import ( "context" "database/sql" + "encoding/json" "errors" "regexp" "strconv" @@ -13,8 +14,10 @@ import ( "github.com/lib/pq" "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) @@ -52,6 +55,7 @@ const ( " aggregate_id," + " aggregate_version," + " creation_date," + + " position," + " event_data," + " editor_user," + " editor_service," + @@ -59,7 +63,8 @@ const ( " instance_id," + " event_sequence," + " previous_aggregate_sequence," + - " previous_aggregate_type_sequence" + + " previous_aggregate_type_sequence," + + " in_tx_order" + ") " + // defines the data to be inserted "SELECT" + @@ -67,17 +72,19 @@ const ( " $2::VARCHAR AS aggregate_type," + " $3::VARCHAR AS aggregate_id," + " $4::VARCHAR AS aggregate_version," + - " statement_timestamp() AS creation_date," + + " hlc_to_timestamp(cluster_logical_timestamp()) AS creation_date," + + " cluster_logical_timestamp() AS position," + " $5::JSONB AS event_data," + " $6::VARCHAR AS editor_user," + " $7::VARCHAR AS editor_service," + " COALESCE((resource_owner), $8::VARCHAR) AS resource_owner," + " $9::VARCHAR AS instance_id," + - " NEXTVAL(CONCAT('eventstore.', (CASE WHEN $9 <> '' THEN CONCAT('i_', $9) ELSE 'system' END), '_seq'))," + + " COALESCE(aggregate_sequence, 0)+1," + " aggregate_sequence AS previous_aggregate_sequence," + - " aggregate_type_sequence AS previous_aggregate_type_sequence " + + " aggregate_type_sequence AS previous_aggregate_type_sequence," + + " $10 AS in_tx_order " + "FROM previous_data " + - "RETURNING id, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, creation_date, resource_owner, instance_id" + "RETURNING id, event_sequence, creation_date, resource_owner, instance_id" uniqueInsert = `INSERT INTO eventstore.unique_constraints ( @@ -97,93 +104,109 @@ const ( WHERE instance_id = $1` ) -type CRDB struct { - *database.DB - AllowOrderByCreationDate bool +// awaitOpenTransactions ensures event ordering, so we don't events younger that open transactions +var ( + awaitOpenTransactionsV1 string + awaitOpenTransactionsV2 string +) + +func awaitOpenTransactions(useV1 bool) string { + if useV1 { + return awaitOpenTransactionsV1 + } + return awaitOpenTransactionsV2 } -func NewCRDB(client *database.DB, allowOrderByCreationDate bool) *CRDB { - return &CRDB{client, allowOrderByCreationDate} +type CRDB struct { + *database.DB +} + +func NewCRDB(client *database.DB) *CRDB { + switch client.Type() { + case "cockroach": + awaitOpenTransactionsV1 = " AND creation_date::TIMESTAMP < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = '" + database.EventstorePusherAppName + "')" + awaitOpenTransactionsV2 = ` AND hlc_to_timestamp("position") < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = '` + database.EventstorePusherAppName + `')` + case "postgres": + awaitOpenTransactionsV1 = ` AND EXTRACT(EPOCH FROM created_at) < (SELECT COALESCE(EXTRACT(EPOCH FROM min(xact_start)), EXTRACT(EPOCH FROM now())) FROM pg_stat_activity WHERE datname = current_database() AND application_name = '` + database.EventstorePusherAppName + `' AND state <> 'idle')` + awaitOpenTransactionsV2 = ` AND "position" < (SELECT COALESCE(EXTRACT(EPOCH FROM min(xact_start)), EXTRACT(EPOCH FROM now())) FROM pg_stat_activity WHERE datname = current_database() AND application_name = '` + database.EventstorePusherAppName + `' AND state <> 'idle')` + } + + return &CRDB{client} } func (db *CRDB) Health(ctx context.Context) error { return db.Ping() } // Push adds all events to the eventstreams of the aggregates. // This call is transaction save. The transaction will be rolled back if one event fails -func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error { - err := crdb.ExecuteTx(ctx, db.DB.DB, nil, func(tx *sql.Tx) error { +func (db *CRDB) Push(ctx context.Context, commands ...eventstore.Command) (events []eventstore.Event, err error) { + events = make([]eventstore.Event, len(commands)) + + err = crdb.ExecuteTx(ctx, db.DB.DB, nil, func(tx *sql.Tx) error { + + var uniqueConstraints []*eventstore.UniqueConstraint + + for i, command := range commands { + if command.Aggregate().InstanceID == "" { + command.Aggregate().InstanceID = authz.GetInstance(ctx).InstanceID() + } + + var payload []byte + if command.Payload() != nil { + payload, err = json.Marshal(command.Payload()) + if err != nil { + return err + } + } + e := &repository.Event{ + Typ: command.Type(), + Data: payload, + EditorUser: command.Creator(), + Version: command.Aggregate().Version, + AggregateID: command.Aggregate().ID, + AggregateType: command.Aggregate().Type, + ResourceOwner: sql.NullString{String: command.Aggregate().ResourceOwner, Valid: command.Aggregate().ResourceOwner != ""}, + InstanceID: command.Aggregate().InstanceID, + } - var ( - previousAggregateSequence Sequence - previousAggregateTypeSequence Sequence - ) - for _, event := range events { err := tx.QueryRowContext(ctx, crdbInsert, - event.Type, - event.AggregateType, - event.AggregateID, - event.Version, - Data(event.Data), - event.EditorUser, - event.EditorService, - event.ResourceOwner, - event.InstanceID, - ).Scan(&event.ID, &event.Sequence, &previousAggregateSequence, &previousAggregateTypeSequence, &event.CreationDate, &event.ResourceOwner, &event.InstanceID) - - event.PreviousAggregateSequence = uint64(previousAggregateSequence) - event.PreviousAggregateTypeSequence = uint64(previousAggregateTypeSequence) + e.Type(), + e.Aggregate().Type, + e.Aggregate().ID, + e.Aggregate().Version, + payload, + e.Creator(), + "zitadel", + e.Aggregate().ResourceOwner, + e.Aggregate().InstanceID, + i, + ).Scan(&e.ID, &e.Seq, &e.CreationDate, &e.ResourceOwner, &e.InstanceID) if err != nil { logging.WithFields( - "aggregate", event.AggregateType, - "aggregateId", event.AggregateID, - "aggregateType", event.AggregateType, - "eventType", event.Type, - "instanceID", event.InstanceID, + "aggregate", e.Aggregate().Type, + "aggregateId", e.Aggregate().ID, + "aggregateType", e.Aggregate().Type, + "eventType", e.Type(), + "instanceID", e.Aggregate().InstanceID, ).WithError(err).Debug("query failed") return caos_errs.ThrowInternal(err, "SQL-SBP37", "unable to create event") } + + uniqueConstraints = append(uniqueConstraints, command.UniqueConstraints()...) + events[i] = e } - err := db.handleUniqueConstraints(ctx, tx, uniqueConstraints...) - if err != nil { - return err - } - return nil + return db.handleUniqueConstraints(ctx, tx, uniqueConstraints...) }) if err != nil && !errors.Is(err, &caos_errs.CaosError{}) { err = caos_errs.ThrowInternal(err, "SQL-DjgtG", "unable to store events") } - return err -} - -var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`) - -func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error { - var sequenceName string - err := db.QueryRowContext(ctx, - func(row *sql.Row) error { - if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) { - return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument") - } - return nil - }, - "SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID, - ) - if err != nil { - return err - } - - if _, err := db.ExecContext(ctx, "CREATE SEQUENCE "+sequenceName); err != nil { - return caos_errs.ThrowInternal(err, "SQL-7gtFA", "Errors.Internal") - } - - return nil + return events, err } // handleUniqueConstraints adds or removes unique constraints -func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueConstraints ...*repository.UniqueConstraint) (err error) { +func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueConstraints ...*eventstore.UniqueConstraint) (err error) { if len(uniqueConstraints) == 0 || (len(uniqueConstraints) == 1 && uniqueConstraints[0] == nil) { return nil } @@ -191,32 +214,32 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC for _, uniqueConstraint := range uniqueConstraints { uniqueConstraint.UniqueField = strings.ToLower(uniqueConstraint.UniqueField) switch uniqueConstraint.Action { - case repository.UniqueConstraintAdd: - _, err := tx.ExecContext(ctx, uniqueInsert, uniqueConstraint.UniqueType, uniqueConstraint.UniqueField, uniqueConstraint.InstanceID) + case eventstore.UniqueConstraintAdd: + _, err := tx.ExecContext(ctx, uniqueInsert, uniqueConstraint.UniqueType, uniqueConstraint.UniqueField, authz.GetInstance(ctx).InstanceID()) if err != nil { logging.WithFields( "unique_type", uniqueConstraint.UniqueType, "unique_field", uniqueConstraint.UniqueField).WithError(err).Info("insert unique constraint failed") if db.isUniqueViolationError(err) { - return caos_errs.ThrowAlreadyExists(err, "SQL-M0dsf", uniqueConstraint.ErrorMessage) + return caos_errs.ThrowAlreadyExists(err, "SQL-wHcEq", uniqueConstraint.ErrorMessage) } return caos_errs.ThrowInternal(err, "SQL-dM9ds", "unable to create unique constraint") } - case repository.UniqueConstraintRemoved: - _, err := tx.ExecContext(ctx, uniqueDelete, uniqueConstraint.UniqueType, uniqueConstraint.UniqueField, uniqueConstraint.InstanceID) + case eventstore.UniqueConstraintRemove: + _, err := tx.ExecContext(ctx, uniqueDelete, uniqueConstraint.UniqueType, uniqueConstraint.UniqueField, authz.GetInstance(ctx).InstanceID()) if err != nil { logging.WithFields( "unique_type", uniqueConstraint.UniqueType, "unique_field", uniqueConstraint.UniqueField).WithError(err).Info("delete unique constraint failed") return caos_errs.ThrowInternal(err, "SQL-6n88i", "unable to remove unique constraint") } - case repository.UniqueConstraintInstanceRemoved: - _, err := tx.ExecContext(ctx, uniqueDeleteInstance, uniqueConstraint.InstanceID) + case eventstore.UniqueConstraintInstanceRemove: + _, err := tx.ExecContext(ctx, uniqueDeleteInstance, authz.GetInstance(ctx).InstanceID()) if err != nil { logging.WithFields( - "instance_id", uniqueConstraint.InstanceID).WithError(err).Info("delete instance unique constraints failed") + "instance_id", authz.GetInstance(ctx).InstanceID()).WithError(err).Info("delete instance unique constraints failed") return caos_errs.ThrowInternal(err, "SQL-6n88i", "unable to remove unique constraints of instance") } } @@ -225,9 +248,16 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC } // Filter returns all events matching the given search query -func (crdb *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery) (events []*repository.Event, err error) { - events = []*repository.Event{} - err = query(ctx, crdb, searchQuery, &events) +func (crdb *CRDB) Filter(ctx context.Context, searchQuery *eventstore.SearchQueryBuilder) (events []eventstore.Event, err error) { + events = make([]eventstore.Event, 0, searchQuery.GetLimit()) + err = query(ctx, crdb, searchQuery, &events, false) + pgErr := new(pgconn.PgError) + // check events2 not exists + if err != nil && errors.As(err, &pgErr) { + if pgErr.Code == "42P01" { + err = query(ctx, crdb, searchQuery, &events, true) + } + } if err != nil { return nil, err } @@ -236,19 +266,16 @@ func (crdb *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuer } // LatestSequence returns the latest sequence found by the search query -func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *repository.SearchQuery) (uint64, error) { - var seq Sequence - err := query(ctx, db, searchQuery, &seq) - if err != nil { - return 0, err - } - return uint64(seq), nil +func (db *CRDB) LatestSequence(ctx context.Context, searchQuery *eventstore.SearchQueryBuilder) (float64, error) { + var position sql.NullFloat64 + err := query(ctx, db, searchQuery, &position, false) + return position.Float64, err } // InstanceIDs returns the instance ids found by the search query -func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *repository.SearchQuery) ([]string, error) { +func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *eventstore.SearchQueryBuilder) ([]string, error) { var ids []string - err := query(ctx, db, searchQuery, &ids) + err := query(ctx, db, searchQuery, &ids, false) if err != nil { return nil, err } @@ -259,70 +286,107 @@ func (db *CRDB) db() *database.DB { return db.DB } -func (db *CRDB) orderByEventSequence(desc bool) string { - if db.AllowOrderByCreationDate { +func (db *CRDB) orderByEventSequence(desc, useV1 bool) string { + if useV1 { if desc { - return " ORDER BY creation_date DESC, event_sequence DESC" + return ` ORDER BY event_sequence DESC` } - - return " ORDER BY creation_date, event_sequence" + return ` ORDER BY event_sequence` } - if desc { - return " ORDER BY event_sequence DESC" + return ` ORDER BY "position" DESC, in_tx_order DESC` } - return " ORDER BY event_sequence" + return ` ORDER BY "position", in_tx_order` } -func (db *CRDB) eventQuery() string { +func (db *CRDB) eventQuery(useV1 bool) string { + if useV1 { + return "SELECT" + + " creation_date" + + ", event_type" + + ", event_sequence" + + ", event_data" + + ", editor_user" + + ", resource_owner" + + ", instance_id" + + ", aggregate_type" + + ", aggregate_id" + + ", aggregate_version" + + " FROM eventstore.events" + } return "SELECT" + - " creation_date" + + " created_at" + ", event_type" + - ", event_sequence" + - ", previous_aggregate_sequence" + - ", previous_aggregate_type_sequence" + - ", event_data" + - ", editor_service" + - ", editor_user" + - ", resource_owner" + + `, "sequence"` + + `, "position"` + + ", payload" + + ", creator" + + `, "owner"` + ", instance_id" + ", aggregate_type" + ", aggregate_id" + - ", aggregate_version" + - " FROM eventstore.events" + ", revision" + + " FROM eventstore.events2" } -func (db *CRDB) maxSequenceQuery() string { - return "SELECT MAX(event_sequence) FROM eventstore.events" +func (db *CRDB) maxSequenceQuery(useV1 bool) string { + if useV1 { + return `SELECT event_sequence FROM eventstore.events` + } + return `SELECT "position" FROM eventstore.events2` } -func (db *CRDB) instanceIDsQuery() string { - return "SELECT DISTINCT instance_id FROM eventstore.events" +func (db *CRDB) instanceIDsQuery(useV1 bool) string { + table := "eventstore.events2" + if useV1 { + table = "eventstore.events" + } + return "SELECT DISTINCT instance_id FROM " + table } -func (db *CRDB) columnName(col repository.Field) string { +func (db *CRDB) columnName(col repository.Field, useV1 bool) string { switch col { case repository.FieldAggregateID: return "aggregate_id" case repository.FieldAggregateType: return "aggregate_type" case repository.FieldSequence: - return "event_sequence" + if useV1 { + return "event_sequence" + } + return `"sequence"` case repository.FieldResourceOwner: - return "resource_owner" + if useV1 { + return "resource_owner" + } + return `"owner"` case repository.FieldInstanceID: return "instance_id" case repository.FieldEditorService: - return "editor_service" + if useV1 { + return "editor_service" + } + return "" case repository.FieldEditorUser: - return "editor_user" + if useV1 { + return "editor_user" + } + return "creator" case repository.FieldEventType: return "event_type" case repository.FieldEventData: - return "event_data" + if useV1 { + return "event_data" + } + return "payload" case repository.FieldCreationDate: - return "creation_date" + if useV1 { + return "creation_date" + } + return "created_at" + case repository.FieldPosition: + return `"position"` default: return "" } diff --git a/internal/eventstore/repository/sql/crdb_test.go b/internal/eventstore/repository/sql/crdb_test.go index 0d8b0ca32..a3f3331a8 100644 --- a/internal/eventstore/repository/sql/crdb_test.go +++ b/internal/eventstore/repository/sql/crdb_test.go @@ -1,12 +1,10 @@ package sql import ( - "context" "database/sql" - "sync" "testing" - "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) @@ -25,28 +23,28 @@ func TestCRDB_placeholder(t *testing.T) { { name: "no placeholders", args: args{ - query: "SELECT * FROM eventstore.events", + query: "SELECT * FROM eventstore.events2", }, res: res{ - query: "SELECT * FROM eventstore.events", + query: "SELECT * FROM eventstore.events2", }, }, { name: "one placeholder", args: args{ - query: "SELECT * FROM eventstore.events WHERE aggregate_type = ?", + query: "SELECT * FROM eventstore.events2 WHERE aggregate_type = ?", }, res: res{ - query: "SELECT * FROM eventstore.events WHERE aggregate_type = $1", + query: "SELECT * FROM eventstore.events2 WHERE aggregate_type = $1", }, }, { name: "multiple placeholders", args: args{ - query: "SELECT * FROM eventstore.events WHERE aggregate_type = ? AND aggregate_id = ? LIMIT ?", + query: "SELECT * FROM eventstore.events2 WHERE aggregate_type = ? AND aggregate_id = ? LIMIT ?", }, res: res{ - query: "SELECT * FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = $2 LIMIT $3", + query: "SELECT * FROM eventstore.events2 WHERE aggregate_type = $1 AND aggregate_id = $2 LIMIT $3", }, }, } @@ -175,6 +173,7 @@ func TestCRDB_columnName(t *testing.T) { } type args struct { field repository.Field + useV1 bool } tests := []struct { name string @@ -212,18 +211,38 @@ func TestCRDB_columnName(t *testing.T) { name: "editor service", args: args{ field: repository.FieldEditorService, + useV1: true, }, res: res{ name: "editor_service", }, }, + { + name: "editor service v2", + args: args{ + field: repository.FieldEditorService, + }, + res: res{ + name: "", + }, + }, { name: "editor user", + args: args{ + field: repository.FieldEditorUser, + useV1: true, + }, + res: res{ + name: "editor_user", + }, + }, + { + name: "editor user v2", args: args{ field: repository.FieldEditorUser, }, res: res{ - name: "editor_user", + name: "creator", }, }, { @@ -239,976 +258,61 @@ func TestCRDB_columnName(t *testing.T) { name: "latest sequence", args: args{ field: repository.FieldSequence, + useV1: true, }, res: res{ name: "event_sequence", }, }, + { + name: "latest sequence v2", + args: args{ + field: repository.FieldSequence, + }, + res: res{ + name: `"sequence"`, + }, + }, { name: "resource owner", + args: args{ + field: repository.FieldResourceOwner, + useV1: true, + }, + res: res{ + name: "resource_owner", + }, + }, + { + name: "resource owner v2", args: args{ field: repository.FieldResourceOwner, }, res: res{ - name: "resource_owner", + name: `"owner"`, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db := &CRDB{} - if got := db.columnName(tt.args.field); got != tt.res.name { + if got := db.columnName(tt.args.field, tt.args.useV1); got != tt.res.name { t.Errorf("CRDB.operation() = %v, want %v", got, tt.res.name) } }) } } -func TestCRDB_Push_OneAggregate(t *testing.T) { - type args struct { - ctx context.Context - events []*repository.Event - uniqueConstraints *repository.UniqueConstraint - uniqueDataType string - uniqueDataField string - uniqueDataInstanceID string - } - type eventsRes struct { - pushedEventsCount int - uniqueCount int - assetCount int - aggType repository.AggregateType - aggID database.StringArray - } - type res struct { - wantErr bool - eventsRes eventsRes - } - tests := []struct { - name string - args args - res res - }{ - { - name: "push 1 event", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "1"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - aggID: []string{"1"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - { - name: "push two events on agg", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "6"), - generateEvent(t, "6"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 2, - aggID: []string{"6"}, - aggType: repository.AggregateType(t.Name()), - }, - }, - }, - { - name: "failed push because context canceled", - args: args{ - ctx: canceledCtx(), - events: []*repository.Event{ - generateEvent(t, "9"), - }, - }, - res: res{ - wantErr: true, - eventsRes: eventsRes{ - pushedEventsCount: 0, - aggID: []string{"9"}, - aggType: repository.AggregateType(t.Name()), - }, - }, - }, - { - name: "push 1 event and add unique constraint", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "10"), - }, - uniqueConstraints: generateAddUniqueConstraint(t, "usernames", "field"), - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - uniqueCount: 1, - aggID: []string{"10"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - { - name: "push 1 event and remove unique constraint", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "11"), - }, - uniqueConstraints: generateRemoveUniqueConstraint(t, "usernames", "testremove"), - uniqueDataType: "usernames", - uniqueDataField: "testremove", - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - uniqueCount: 0, - aggID: []string{"11"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - { - name: "push 1 event and remove instance unique constraints", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "12"), - }, - uniqueConstraints: generateRemoveInstanceUniqueConstraints(t, "instanceID"), - uniqueDataType: "usernames", - uniqueDataField: "testremove", - uniqueDataInstanceID: "instanceID", - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - uniqueCount: 0, - aggID: []string{"12"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - { - name: "push 1 event and add asset", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "13"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - assetCount: 1, - aggID: []string{"13"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - { - name: "push 1 event and remove asset", - args: args{ - ctx: context.Background(), - events: []*repository.Event{ - generateEvent(t, "14"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 1, - assetCount: 0, - aggID: []string{"14"}, - aggType: repository.AggregateType(t.Name()), - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - if tt.args.uniqueDataType != "" && tt.args.uniqueDataField != "" { - err := fillUniqueData(tt.args.uniqueDataType, tt.args.uniqueDataField, tt.args.uniqueDataInstanceID) - if err != nil { - t.Error("unable to prefill insert unique data: ", err) - return - } - } - if err := db.Push(tt.args.ctx, tt.args.events, tt.args.uniqueConstraints); (err != nil) != tt.res.wantErr { - t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) - } - - countEventRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = $1 AND aggregate_id = ANY($2)", tt.res.eventsRes.aggType, tt.res.eventsRes.aggID) - var eventCount int - err := countEventRow.Scan(&eventCount) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - if eventCount != tt.res.eventsRes.pushedEventsCount { - t.Errorf("expected push count %d got %d", tt.res.eventsRes.pushedEventsCount, eventCount) - } - if tt.args.uniqueConstraints != nil { - countUniqueRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.unique_constraints where unique_type = $1 AND unique_field = $2 AND instance_id = $3", tt.args.uniqueConstraints.UniqueType, tt.args.uniqueConstraints.UniqueField, tt.args.uniqueConstraints.InstanceID) - var uniqueCount int - err := countUniqueRow.Scan(&uniqueCount) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - if uniqueCount != tt.res.eventsRes.uniqueCount { - t.Errorf("expected unique count %d got %d", tt.res.eventsRes.uniqueCount, uniqueCount) - } - } - }) - } -} - -func TestCRDB_Push_MultipleAggregate(t *testing.T) { - type args struct { - events []*repository.Event - } - type eventsRes struct { - pushedEventsCount int - aggType database.StringArray - aggID database.StringArray - } - type res struct { - wantErr bool - eventsRes eventsRes - } - tests := []struct { - name string - args args - res res - }{ - { - name: "push two aggregates", - args: args{ - events: []*repository.Event{ - generateEvent(t, "100"), - generateEvent(t, "101"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 2, - aggID: []string{"100", "101"}, - aggType: database.StringArray{t.Name()}, - }, - }, - }, - { - name: "push two aggregates both multiple events", - args: args{ - events: []*repository.Event{ - generateEvent(t, "102"), - generateEvent(t, "102"), - generateEvent(t, "103"), - generateEvent(t, "103"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 4, - aggID: []string{"102", "103"}, - aggType: database.StringArray{t.Name()}, - }, - }, - }, - { - name: "push two aggregates mixed multiple events", - args: args{ - events: []*repository.Event{ - generateEvent(t, "106"), - generateEvent(t, "106"), - generateEvent(t, "106"), - generateEvent(t, "106"), - generateEvent(t, "107"), - generateEvent(t, "107"), - generateEvent(t, "107"), - generateEvent(t, "107"), - generateEvent(t, "108"), - generateEvent(t, "108"), - generateEvent(t, "108"), - generateEvent(t, "108"), - }, - }, - res: res{ - wantErr: false, - eventsRes: eventsRes{ - pushedEventsCount: 12, - aggID: []string{"106", "107", "108"}, - aggType: database.StringArray{t.Name()}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - if err := db.Push(context.Background(), tt.args.events); (err != nil) != tt.res.wantErr { - t.Errorf("CRDB.Push() error = %v, wantErr %v", err, tt.res.wantErr) - } - - countRow := testCRDBClient.QueryRow("SELECT COUNT(*) FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2)", tt.res.eventsRes.aggType, tt.res.eventsRes.aggID) - var count int - err := countRow.Scan(&count) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - if count != tt.res.eventsRes.pushedEventsCount { - t.Errorf("expected push count %d got %d", tt.res.eventsRes.pushedEventsCount, count) - } - }) - } -} - -func TestCRDB_CreateInstance(t *testing.T) { - type args struct { - instanceID string - } - type res struct { - wantErr bool - exists bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "no number", - args: args{ - instanceID: "asdf;use defaultdb;DROP DATABASE zitadel;--", - }, - res: res{ - wantErr: true, - exists: false, - }, - }, - { - name: "no instance id", - args: args{ - instanceID: "", - }, - res: res{ - wantErr: true, - exists: false, - }, - }, - { - name: "correct number", - args: args{ - instanceID: "1235", - }, - res: res{ - wantErr: false, - exists: true, - }, - }, - { - name: "correct text", - args: args{ - instanceID: "system", - }, - res: res{ - wantErr: false, - exists: true, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{DB: testCRDBClient}, - } - - if err := db.CreateInstance(context.Background(), tt.args.instanceID); (err != nil) != tt.res.wantErr { - t.Errorf("CRDB.CreateInstance() error = %v, wantErr %v", err, tt.res.wantErr) - } - - sequenceRow := testCRDBClient.QueryRow("SELECT EXISTS(SELECT 1 FROM [SHOW SEQUENCES] WHERE sequence_schema = 'eventstore' AND sequence_name like $1)", "i_"+tt.args.instanceID+"%") - var exists bool - err := sequenceRow.Scan(&exists) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - if exists != tt.res.exists { - t.Errorf("expected exists %v got %v", tt.res.exists, exists) - } - }) - } -} - -func TestCRDB_Push_Parallel(t *testing.T) { - type args struct { - events [][]*repository.Event - } - type eventsRes struct { - pushedEventsCount int - aggTypes database.StringArray - aggIDs database.StringArray - } - type res struct { - errCount int - eventsRes eventsRes - } - tests := []struct { - name string - args args - res res - }{ - { - name: "clients push different aggregates", - args: args{ - events: [][]*repository.Event{ - { - generateEvent(t, "200"), - generateEvent(t, "200"), - generateEvent(t, "200"), - generateEvent(t, "201"), - generateEvent(t, "201"), - generateEvent(t, "201"), - }, - { - generateEvent(t, "202"), - generateEvent(t, "203"), - generateEvent(t, "203"), - }, - }, - }, - res: res{ - errCount: 0, - eventsRes: eventsRes{ - aggIDs: []string{"200", "201", "202", "203"}, - pushedEventsCount: 9, - aggTypes: database.StringArray{t.Name()}, - }, - }, - }, - { - name: "clients push same aggregates", - args: args{ - events: [][]*repository.Event{ - { - generateEvent(t, "204"), - generateEvent(t, "204"), - }, - { - generateEvent(t, "204"), - generateEvent(t, "204"), - }, - { - generateEvent(t, "205"), - generateEvent(t, "205"), - generateEvent(t, "205"), - generateEvent(t, "206"), - generateEvent(t, "206"), - generateEvent(t, "206"), - }, - { - generateEvent(t, "204"), - generateEvent(t, "205"), - generateEvent(t, "205"), - generateEvent(t, "206"), - }, - }, - }, - res: res{ - errCount: 0, - eventsRes: eventsRes{ - aggIDs: []string{"204", "205", "206"}, - pushedEventsCount: 14, - aggTypes: database.StringArray{t.Name()}, - }, - }, - }, - { - name: "clients push different aggregates", - args: args{ - events: [][]*repository.Event{ - { - generateEvent(t, "207"), - generateEvent(t, "207"), - generateEvent(t, "207"), - generateEvent(t, "207"), - generateEvent(t, "207"), - generateEvent(t, "207"), - }, - { - generateEvent(t, "208"), - generateEvent(t, "208"), - generateEvent(t, "208"), - generateEvent(t, "208"), - generateEvent(t, "208"), - }, - }, - }, - res: res{ - errCount: 0, - eventsRes: eventsRes{ - aggIDs: []string{"207", "208"}, - pushedEventsCount: 11, - aggTypes: database.StringArray{t.Name()}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - wg := sync.WaitGroup{} - - errs := make([]error, 0, tt.res.errCount) - errsMu := sync.Mutex{} - for _, events := range tt.args.events { - wg.Add(1) - go func(events []*repository.Event) { - err := db.Push(context.Background(), events, nil) - if err != nil { - errsMu.Lock() - errs = append(errs, err) - errsMu.Unlock() - } - - wg.Done() - }(events) - } - wg.Wait() - - if len(errs) != tt.res.errCount { - t.Errorf("CRDB.Push() error count = %d, wanted err count %d, errs: %v", len(errs), tt.res.errCount, errs) - } - - rows, err := testCRDBClient.Query("SELECT event_data FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2) order by event_sequence", tt.res.eventsRes.aggTypes, tt.res.eventsRes.aggIDs) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - var count int - - for rows.Next() { - count++ - data := make(Data, 0) - - err := rows.Scan(&data) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - t.Logf("inserted data: %v", string(data)) - } - if count != tt.res.eventsRes.pushedEventsCount { - t.Errorf("expected push count %d got %d", tt.res.eventsRes.pushedEventsCount, count) - } - }) - } -} - -func TestCRDB_Filter(t *testing.T) { - type args struct { - searchQuery *repository.SearchQuery - } - type fields struct { - existingEvents []*repository.Event - } - type res struct { - eventCount int - } - tests := []struct { - name string - fields fields - args args - res res - wantErr bool - }{ - { - name: "aggregate type filter no events", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, "not found", repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - existingEvents: []*repository.Event{ - generateEvent(t, "300"), - generateEvent(t, "300"), - generateEvent(t, "300"), - }, - }, - res: res{ - eventCount: 0, - }, - wantErr: false, - }, - { - name: "aggregate type and id filter events found", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, t.Name(), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, "303", repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - existingEvents: []*repository.Event{ - generateEvent(t, "303"), - generateEvent(t, "303"), - generateEvent(t, "303"), - generateEvent(t, "305"), - }, - }, - res: res{ - eventCount: 3, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - - // setup initial data for query - if err := db.Push(context.Background(), tt.fields.existingEvents); err != nil { - t.Errorf("error in setup = %v", err) - return - } - - events, err := db.Filter(context.Background(), tt.args.searchQuery) - if (err != nil) != tt.wantErr { - t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr) - } - - if len(events) != tt.res.eventCount { - t.Errorf("CRDB.query() expected event count: %d got %d", tt.res.eventCount, len(events)) - } - }) - } -} - -func TestCRDB_LatestSequence(t *testing.T) { - type args struct { - searchQuery *repository.SearchQuery - } - type fields struct { - existingEvents []*repository.Event - } - type res struct { - sequence uint64 - } - tests := []struct { - name string - fields fields - args args - res res - wantErr bool - }{ - { - name: "aggregate type filter no sequence", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsMaxSequence, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, "not found", repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - existingEvents: []*repository.Event{ - generateEvent(t, "400"), - generateEvent(t, "400"), - generateEvent(t, "400"), - }, - }, - res: res{ - sequence: 0, - }, - wantErr: false, - }, - { - name: "aggregate type filter sequence", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsMaxSequence, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, t.Name(), repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - existingEvents: []*repository.Event{ - generateEvent(t, "401"), - generateEvent(t, "401"), - generateEvent(t, "401"), - }, - }, - res: res{ - sequence: 3, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - - // setup initial data for query - if err := db.Push(context.Background(), tt.fields.existingEvents); err != nil { - t.Errorf("error in setup = %v", err) - return - } - - sequence, err := db.LatestSequence(context.Background(), tt.args.searchQuery) - if (err != nil) != tt.wantErr { - t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr) - } - - if sequence < tt.res.sequence { - t.Errorf("CRDB.query() expected sequence: %d got %d", tt.res.sequence, sequence) - } - }) - } -} - -func TestCRDB_Push_ResourceOwner(t *testing.T) { - type args struct { - events []*repository.Event - } - type res struct { - resourceOwners database.StringArray - } - type fields struct { - aggregateIDs database.StringArray - aggregateType string - } - tests := []struct { - name string - args args - res res - fields fields - }{ - { - name: "two events of same aggregate same resource owner", - args: args{ - events: []*repository.Event{ - generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"500"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "caos"}, - }, - }, - { - name: "two events of different aggregate same resource owner", - args: args{ - events: []*repository.Event{ - generateEvent(t, "501", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "502", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"501", "502"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "caos"}, - }, - }, - { - name: "two events of different aggregate different resource owner", - args: args{ - events: []*repository.Event{ - generateEvent(t, "503", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "504", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"503", "504"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "zitadel"}, - }, - }, - { - name: "events of different aggregate different resource owner", - args: args{ - events: []*repository.Event{ - generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), - generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"505", "506"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "caos", "zitadel", "zitadel"}, - }, - }, - { - name: "events of different aggregate different resource owner per event", - args: args{ - events: []*repository.Event{ - generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), - generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), - generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"507", "508"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "caos", "zitadel", "zitadel"}, - }, - }, - { - name: "events of one aggregate different resource owner per event", - args: args{ - events: []*repository.Event{ - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), - }, - }, - fields: fields{ - aggregateIDs: []string{"509"}, - aggregateType: t.Name(), - }, - res: res{ - resourceOwners: []string{"caos", "caos", "caos", "caos"}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - db := &CRDB{ - DB: &database.DB{ - DB: testCRDBClient, - Database: new(testDB), - }, - } - if err := db.Push(context.Background(), tt.args.events); err != nil { - t.Errorf("CRDB.Push() error = %v", err) - } - - if len(tt.args.events) != len(tt.res.resourceOwners) { - t.Errorf("length of events (%d) and resource owners (%d) must be equal", len(tt.args.events), len(tt.res.resourceOwners)) - return - } - - for i, event := range tt.args.events { - if event.ResourceOwner.String != tt.res.resourceOwners[i] { - t.Errorf("resource owner not expected want: %q got: %q", tt.res.resourceOwners[i], event.ResourceOwner.String) - } - } - - rows, err := testCRDBClient.Query("SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY creation_date, event_sequence", tt.fields.aggregateType, tt.fields.aggregateIDs) - if err != nil { - t.Error("unable to query inserted rows: ", err) - return - } - - eventCount := 0 - for i := 0; rows.Next(); i++ { - var resourceOwner string - err = rows.Scan(&resourceOwner) - if err != nil { - t.Error("unable to scan row: ", err) - return - } - if resourceOwner != tt.res.resourceOwners[i] { - t.Errorf("unexpected resource owner in queried event. want %q, got: %q", tt.res.resourceOwners[i], resourceOwner) - } - eventCount++ - } - - if eventCount != len(tt.res.resourceOwners) { - t.Errorf("wrong queried event count: want %d, got %d", len(tt.res.resourceOwners), eventCount) - } - }) - } -} - -func canceledCtx() context.Context { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - return ctx -} - func generateEvent(t *testing.T, aggregateID string, opts ...func(*repository.Event)) *repository.Event { t.Helper() e := &repository.Event{ AggregateID: aggregateID, - AggregateType: repository.AggregateType(t.Name()), - EditorService: "svc", + AggregateType: eventstore.AggregateType(t.Name()), EditorUser: "user", ResourceOwner: sql.NullString{String: "ro", Valid: true}, - Type: "test.created", + Typ: "test.created", Version: "v1", + Pos: 42, } for _, opt := range opts { @@ -1217,36 +321,3 @@ func generateEvent(t *testing.T, aggregateID string, opts ...func(*repository.Ev return e } - -func generateAddUniqueConstraint(t *testing.T, table, uniqueField string) *repository.UniqueConstraint { - t.Helper() - e := &repository.UniqueConstraint{ - UniqueType: table, - UniqueField: uniqueField, - Action: repository.UniqueConstraintAdd, - } - - return e -} - -func generateRemoveUniqueConstraint(t *testing.T, table, uniqueField string) *repository.UniqueConstraint { - t.Helper() - e := &repository.UniqueConstraint{ - UniqueType: table, - UniqueField: uniqueField, - InstanceID: "", - Action: repository.UniqueConstraintRemoved, - } - - return e -} - -func generateRemoveInstanceUniqueConstraints(t *testing.T, instanceID string) *repository.UniqueConstraint { - t.Helper() - e := &repository.UniqueConstraint{ - InstanceID: instanceID, - Action: repository.UniqueConstraintInstanceRemoved, - } - - return e -} diff --git a/internal/eventstore/repository/sql/local_crdb_test.go b/internal/eventstore/repository/sql/local_crdb_test.go index 50c3465b1..b1aa48588 100644 --- a/internal/eventstore/repository/sql/local_crdb_test.go +++ b/internal/eventstore/repository/sql/local_crdb_test.go @@ -37,14 +37,14 @@ func TestMain(m *testing.M) { ts.Stop() }() - if err = initDB(testCRDBClient); err != nil { + if err = initDB(&database.DB{DB: testCRDBClient, Database: &cockroach.Config{Database: "zitadel"}}); err != nil { logging.WithFields("error", err).Fatal("migrations failed") } os.Exit(m.Run()) } -func initDB(db *sql.DB) error { +func initDB(db *database.DB) error { config := new(database.Config) config.SetConnector(&cockroach.Config{User: cockroach.User{Username: "zitadel"}, Database: "zitadel"}) @@ -60,11 +60,13 @@ func initDB(db *sql.DB) error { return err } - return initialise.VerifyZitadel(db, *config) -} + err = initialise.VerifyZitadel(db, *config) + if err != nil { + return err + } -func fillUniqueData(unique_type, field, instanceID string) error { - _, err := testCRDBClient.Exec("INSERT INTO eventstore.unique_constraints (unique_type, unique_field, instance_id) VALUES ($1, $2, $3)", unique_type, field, instanceID) + // create old events + _, err = db.Exec(oldEventsTable) return err } @@ -76,4 +78,26 @@ func (*testDB) DatabaseName() string { return "db" } func (*testDB) Username() string { return "user" } -func (*testDB) Type() string { return "type" } +func (*testDB) Type() string { return "cockroach" } + +const oldEventsTable = `CREATE TABLE IF NOT EXISTS eventstore.events ( + id UUID DEFAULT gen_random_uuid() + , event_type TEXT NOT NULL + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + , aggregate_version TEXT NOT NULL + , event_sequence BIGINT NOT NULL + , previous_aggregate_sequence BIGINT + , previous_aggregate_type_sequence INT8 + , creation_date TIMESTAMPTZ NOT NULL DEFAULT now() + , created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp() + , event_data JSONB + , editor_user TEXT NOT NULL + , editor_service TEXT + , resource_owner TEXT NOT NULL + , instance_id TEXT NOT NULL + , "position" DECIMAL NOT NULL + , in_tx_order INTEGER NOT NULL + + , PRIMARY KEY (instance_id, aggregate_type, aggregate_id, event_sequence DESC) +);` diff --git a/internal/eventstore/repository/sql/query.go b/internal/eventstore/repository/sql/query.go index 8bc3b3ac9..144a414fd 100644 --- a/internal/eventstore/repository/sql/query.go +++ b/internal/eventstore/repository/sql/query.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "strconv" "strings" "github.com/zitadel/logging" @@ -14,19 +15,20 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/database/dialect" z_errors "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) type querier interface { - columnName(repository.Field) string + columnName(field repository.Field, useV1 bool) string operation(repository.Operation) string conditionFormat(repository.Operation) string placeholder(query string) string - eventQuery() string - maxSequenceQuery() string - instanceIDsQuery() string + eventQuery(useV1 bool) string + maxSequenceQuery(useV1 bool) string + instanceIDsQuery(useV1 bool) string db() *database.DB - orderByEventSequence(desc bool) string + orderByEventSequence(desc, useV1 bool) string dialect.Database } @@ -52,25 +54,38 @@ func (t *tx) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, return rows.Err() } -func query(ctx context.Context, criteria querier, searchQuery *repository.SearchQuery, dest interface{}) error { - query, rowScanner := prepareColumns(criteria, searchQuery.Columns) - where, values := prepareCondition(criteria, searchQuery.Filters) +func query(ctx context.Context, criteria querier, searchQuery *eventstore.SearchQueryBuilder, dest interface{}, useV1 bool) error { + q, err := repository.QueryFromBuilder(searchQuery) + if err != nil { + return err + } + query, rowScanner := prepareColumns(criteria, q.Columns, useV1) + where, values := prepareConditions(criteria, q, useV1) if where == "" || query == "" { return z_errors.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") } - if searchQuery.Tx == nil { - if travel := prepareTimeTravel(ctx, criteria, searchQuery.AllowTimeTravel); travel != "" { + if q.Tx == nil { + if travel := prepareTimeTravel(ctx, criteria, q.AllowTimeTravel); travel != "" { query += travel } } query += where - if searchQuery.Columns == repository.ColumnsEvent { - query += criteria.orderByEventSequence(searchQuery.Desc) + // instead of using the max function of the database (which doesn't work for postgres) + // we select the most recent row + if q.Columns == eventstore.ColumnsMaxSequence { + q.Limit = 1 + q.Desc = true } - if searchQuery.Limit > 0 { - values = append(values, searchQuery.Limit) + switch q.Columns { + case eventstore.ColumnsEvent, + eventstore.ColumnsMaxSequence: + query += criteria.orderByEventSequence(q.Desc, useV1) + } + + if q.Limit > 0 { + values = append(values, q.Limit) query += " LIMIT ?" } @@ -80,11 +95,11 @@ func query(ctx context.Context, criteria querier, searchQuery *repository.Search QueryContext(context.Context, func(rows *sql.Rows) error, string, ...interface{}) error } contextQuerier = criteria.db() - if searchQuery.Tx != nil { - contextQuerier = &tx{Tx: searchQuery.Tx} + if q.Tx != nil { + contextQuerier = &tx{Tx: q.Tx} } - err := contextQuerier.QueryContext(ctx, + err = contextQuerier.QueryContext(ctx, func(rows *sql.Rows) error { for rows.Next() { err := rowScanner(rows.Scan, dest) @@ -102,14 +117,14 @@ func query(ctx context.Context, criteria querier, searchQuery *repository.Search return nil } -func prepareColumns(criteria querier, columns repository.Columns) (string, func(s scan, dest interface{}) error) { +func prepareColumns(criteria querier, columns eventstore.Columns, useV1 bool) (string, func(s scan, dest interface{}) error) { switch columns { - case repository.ColumnsMaxSequence: - return criteria.maxSequenceQuery(), maxSequenceScanner - case repository.ColumnsInstanceIDs: - return criteria.instanceIDsQuery(), instanceIDsScanner - case repository.ColumnsEvent: - return criteria.eventQuery(), eventsScanner + case eventstore.ColumnsMaxSequence: + return criteria.maxSequenceQuery(useV1), maxSequenceScanner + case eventstore.ColumnsInstanceIDs: + return criteria.instanceIDsQuery(useV1), instanceIDsScanner + case eventstore.ColumnsEvent: + return criteria.eventQuery(useV1), eventsScanner(useV1) default: return "", nil } @@ -124,11 +139,11 @@ func prepareTimeTravel(ctx context.Context, criteria querier, allow bool) string } func maxSequenceScanner(row scan, dest interface{}) (err error) { - sequence, ok := dest.(*Sequence) + position, ok := dest.(*sql.NullFloat64) if !ok { - return z_errors.ThrowInvalidArgument(nil, "SQL-NBjA9", "type must be sequence") + return z_errors.ThrowInvalidArgumentf(nil, "SQL-NBjA9", "type must be sql.NullInt64 got: %T", dest) } - err = row(sequence) + err = row(position) if err == nil || errors.Is(err, sql.ErrNoRows) { return nil } @@ -151,84 +166,139 @@ func instanceIDsScanner(scanner scan, dest interface{}) (err error) { return nil } -func eventsScanner(scanner scan, dest interface{}) (err error) { - events, ok := dest.(*[]*repository.Event) - if !ok { - return z_errors.ThrowInvalidArgument(nil, "SQL-4GP6F", "type must be event") - } - var ( - previousAggregateSequence Sequence - previousAggregateTypeSequence Sequence - ) - data := make(Data, 0) - event := new(repository.Event) - - err = scanner( - &event.CreationDate, - &event.Type, - &event.Sequence, - &previousAggregateSequence, - &previousAggregateTypeSequence, - &data, - &event.EditorService, - &event.EditorUser, - &event.ResourceOwner, - &event.InstanceID, - &event.AggregateType, - &event.AggregateID, - &event.Version, - ) - - if err != nil { - logging.New().WithError(err).Warn("unable to scan row") - return z_errors.ThrowInternal(err, "SQL-M0dsf", "unable to scan row") - } - - event.PreviousAggregateSequence = uint64(previousAggregateSequence) - event.PreviousAggregateTypeSequence = uint64(previousAggregateTypeSequence) - event.Data = make([]byte, len(data)) - copy(event.Data, data) - - *events = append(*events, event) - - return nil -} - -func prepareCondition(criteria querier, filters [][]*repository.Filter) (clause string, values []interface{}) { - values = make([]interface{}, 0, len(filters)) - - if len(filters) == 0 { - return clause, values - } - - clauses := make([]string, len(filters)) - for idx, filter := range filters { - subClauses := make([]string, 0, len(filter)) - for _, f := range filter { - value := f.Value - switch value.(type) { - case map[string]interface{}: - var err error - value, err = json.Marshal(value) - if err != nil { - logging.WithError(err).Warn("unable to marshal search value") - continue - } - } - - subClauses = append(subClauses, getCondition(criteria, f)) - if subClauses[len(subClauses)-1] == "" { - return "", nil - } - values = append(values, value) +func eventsScanner(useV1 bool) func(scanner scan, dest interface{}) (err error) { + return func(scanner scan, dest interface{}) (err error) { + events, ok := dest.(*[]eventstore.Event) + if !ok { + return z_errors.ThrowInvalidArgument(nil, "SQL-4GP6F", "type must be event") } - clauses[idx] = "( " + strings.Join(subClauses, " AND ") + " )" + event := new(repository.Event) + data := sql.RawBytes{} + position := new(sql.NullFloat64) + + if useV1 { + err = scanner( + &event.CreationDate, + &event.Typ, + &event.Seq, + &data, + &event.EditorUser, + &event.ResourceOwner, + &event.InstanceID, + &event.AggregateType, + &event.AggregateID, + &event.Version, + ) + } else { + var revision uint8 + err = scanner( + &event.CreationDate, + &event.Typ, + &event.Seq, + position, + &data, + &event.EditorUser, + &event.ResourceOwner, + &event.InstanceID, + &event.AggregateType, + &event.AggregateID, + &revision, + ) + event.Version = eventstore.Version("v" + strconv.Itoa(int(revision))) + } + + if err != nil { + logging.New().WithError(err).Warn("unable to scan row") + return z_errors.ThrowInternal(err, "SQL-M0dsf", "unable to scan row") + } + + event.Data = make([]byte, len(data)) + copy(event.Data, data) + event.Pos = position.Float64 + + *events = append(*events, event) + + return nil } - return " WHERE " + strings.Join(clauses, " OR "), values } -func getCondition(cond querier, filter *repository.Filter) (condition string) { - field := cond.columnName(filter.Field) +func prepareConditions(criteria querier, query *repository.SearchQuery, useV1 bool) (string, []any) { + clauses, args := prepareQuery(criteria, useV1, query.InstanceID, query.ExcludedInstances) + if clauses != "" && len(query.SubQueries) > 0 { + clauses += " AND " + } + subClauses := make([]string, len(query.SubQueries)) + for i, filters := range query.SubQueries { + var subArgs []any + subClauses[i], subArgs = prepareQuery(criteria, useV1, filters...) + // an error is thrown in [query] + if subClauses[i] == "" { + return "", nil + } + if len(query.SubQueries) > 1 && len(subArgs) > 1 { + subClauses[i] = "(" + subClauses[i] + ")" + } + args = append(args, subArgs...) + } + if len(subClauses) == 1 { + clauses += subClauses[0] + } else if len(subClauses) > 1 { + clauses += "(" + strings.Join(subClauses, " OR ") + ")" + } + + additionalClauses, additionalArgs := prepareQuery(criteria, useV1, query.Position, query.Owner, query.Sequence, query.CreatedAt, query.Creator) + if additionalClauses != "" { + if clauses != "" { + clauses += " AND " + } + clauses += additionalClauses + args = append(args, additionalArgs...) + } + + if query.AwaitOpenTransactions { + clauses += awaitOpenTransactions(useV1) + } + + if clauses == "" { + return "", nil + } + + return " WHERE " + clauses, args +} + +func prepareQuery(criteria querier, useV1 bool, filters ...*repository.Filter) (_ string, args []any) { + clauses := make([]string, 0, len(filters)) + args = make([]any, 0, len(filters)) + for _, filter := range filters { + if filter == nil { + continue + } + arg := filter.Value + + // marshal if payload filter + if filter.Field == repository.FieldEventData { + var err error + arg, err = json.Marshal(arg) + if err != nil { + logging.WithError(err).Warn("unable to marshal search value") + continue + } + + } + + clauses = append(clauses, getCondition(criteria, filter, useV1)) + // if mapping failed an error is thrown in [query] + if clauses[len(clauses)-1] == "" { + return "", nil + } + args = append(args, arg) + } + + return strings.Join(clauses, " AND "), args +} + +func getCondition(cond querier, filter *repository.Filter, useV1 bool) (condition string) { + field := cond.columnName(filter.Field, useV1) operation := cond.operation(filter.Operation) if field == "" || operation == "" { return "" diff --git a/internal/eventstore/repository/sql/query_test.go b/internal/eventstore/repository/sql/query_test.go index d7438ed27..478191ec1 100644 --- a/internal/eventstore/repository/sql/query_test.go +++ b/internal/eventstore/repository/sql/query_test.go @@ -5,13 +5,16 @@ import ( "database/sql" "database/sql/driver" "reflect" + "strconv" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/database/cockroach" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) @@ -32,38 +35,38 @@ func Test_getCondition(t *testing.T) { { name: "greater", args: args{filter: repository.NewFilter(repository.FieldSequence, 0, repository.OperationGreater)}, - want: "event_sequence > ?", + want: `"sequence" > ?`, }, { name: "less", args: args{filter: repository.NewFilter(repository.FieldSequence, 5000, repository.OperationLess)}, - want: "event_sequence < ?", + want: `"sequence" < ?`, }, { name: "in list", - args: args{filter: repository.NewFilter(repository.FieldAggregateType, []repository.AggregateType{"movies", "actors"}, repository.OperationIn)}, + args: args{filter: repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"movies", "actors"}, repository.OperationIn)}, want: "aggregate_type = ANY(?)", }, { name: "invalid operation", - args: args{filter: repository.NewFilter(repository.FieldAggregateType, []repository.AggregateType{"movies", "actors"}, repository.Operation(-1))}, + args: args{filter: repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"movies", "actors"}, repository.Operation(-1))}, want: "", }, { name: "invalid field", - args: args{filter: repository.NewFilter(repository.Field(-1), []repository.AggregateType{"movies", "actors"}, repository.OperationEquals)}, + args: args{filter: repository.NewFilter(repository.Field(-1), []eventstore.AggregateType{"movies", "actors"}, repository.OperationEquals)}, want: "", }, { name: "invalid field and operation", - args: args{filter: repository.NewFilter(repository.Field(-1), []repository.AggregateType{"movies", "actors"}, repository.Operation(-1))}, + args: args{filter: repository.NewFilter(repository.Field(-1), []eventstore.AggregateType{"movies", "actors"}, repository.Operation(-1))}, want: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db := &CRDB{} - if got := getCondition(db, tt.args.filter); got != tt.want { + if got := getCondition(db, tt.args.filter, false); got != tt.want { t.Errorf("getCondition() = %v, want %v", got, tt.want) } }) @@ -75,9 +78,10 @@ func Test_prepareColumns(t *testing.T) { dbRow []interface{} } type args struct { - columns repository.Columns + columns eventstore.Columns dest interface{} dbErr error + useV1 bool } type res struct { query string @@ -92,7 +96,7 @@ func Test_prepareColumns(t *testing.T) { }{ { name: "invalid columns", - args: args{columns: repository.Columns(-1)}, + args: args{columns: eventstore.Columns(-1)}, res: res{ query: "", dbErr: func(err error) bool { return err == nil }, @@ -101,64 +105,114 @@ func Test_prepareColumns(t *testing.T) { { name: "max column", args: args{ - columns: repository.ColumnsMaxSequence, - dest: new(Sequence), + columns: eventstore.ColumnsMaxSequence, + dest: new(sql.NullFloat64), + useV1: true, }, res: res{ - query: "SELECT MAX(event_sequence) FROM eventstore.events", - expected: Sequence(5), + query: `SELECT event_sequence FROM eventstore.events`, + expected: sql.NullFloat64{Float64: 43, Valid: true}, }, fields: fields{ - dbRow: []interface{}{Sequence(5)}, + dbRow: []interface{}{sql.NullFloat64{Float64: 43, Valid: true}}, + }, + }, + { + name: "max column v2", + args: args{ + columns: eventstore.ColumnsMaxSequence, + dest: new(sql.NullFloat64), + }, + res: res{ + query: `SELECT "position" FROM eventstore.events2`, + expected: sql.NullFloat64{Float64: 43, Valid: true}, + }, + fields: fields{ + dbRow: []interface{}{sql.NullFloat64{Float64: 43, Valid: true}}, }, }, { name: "max sequence wrong dest type", args: args{ - columns: repository.ColumnsMaxSequence, + columns: eventstore.ColumnsMaxSequence, dest: new(uint64), }, res: res{ - query: "SELECT MAX(event_sequence) FROM eventstore.events", + query: `SELECT "position" FROM eventstore.events2`, dbErr: errors.IsErrorInvalidArgument, }, }, { name: "events", args: args{ - columns: repository.ColumnsEvent, - dest: &[]*repository.Event{}, + columns: eventstore.ColumnsEvent, + dest: &[]eventstore.Event{}, + useV1: true, }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", - expected: []*repository.Event{ - {AggregateID: "hodor", AggregateType: "user", Sequence: 5, Data: make(Data, 0)}, + query: `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events`, + expected: []eventstore.Event{ + &repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Data: make(sql.RawBytes, 0)}, }, }, fields: fields{ - dbRow: []interface{}{time.Time{}, repository.EventType(""), uint64(5), Sequence(0), Sequence(0), Data(nil), "", "", sql.NullString{String: ""}, "", repository.AggregateType("user"), "hodor", repository.Version("")}, + dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", eventstore.Version("")}, + }, + }, + { + name: "events v2", + args: args{ + columns: eventstore.ColumnsEvent, + dest: &[]eventstore.Event{}, + }, + res: res{ + query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`, + expected: []eventstore.Event{ + &repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: 42, Data: make(sql.RawBytes, 0), Version: "v1"}, + }, + }, + fields: fields{ + dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), sql.NullFloat64{Float64: 42, Valid: true}, sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)}, + }, + }, + { + name: "event null position", + args: args{ + columns: eventstore.ColumnsEvent, + dest: &[]eventstore.Event{}, + }, + res: res{ + query: `SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2`, + expected: []eventstore.Event{ + &repository.Event{AggregateID: "hodor", AggregateType: "user", Seq: 5, Pos: 0, Data: make(sql.RawBytes, 0), Version: "v1"}, + }, + }, + fields: fields{ + dbRow: []interface{}{time.Time{}, eventstore.EventType(""), uint64(5), sql.NullFloat64{Float64: 0, Valid: false}, sql.RawBytes(nil), "", sql.NullString{}, "", eventstore.AggregateType("user"), "hodor", uint8(1)}, }, }, { name: "events wrong dest type", args: args{ - columns: repository.ColumnsEvent, + columns: eventstore.ColumnsEvent, dest: []*repository.Event{}, + useV1: true, }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", + query: `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events`, dbErr: errors.IsErrorInvalidArgument, }, }, { name: "event query error", args: args{ - columns: repository.ColumnsEvent, - dest: &[]*repository.Event{}, + columns: eventstore.ColumnsEvent, + dest: &[]eventstore.Event{}, dbErr: sql.ErrConnDone, + useV1: true, }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", + query: `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events`, dbErr: errors.IsInternal, }, }, @@ -166,7 +220,7 @@ func Test_prepareColumns(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { crdb := &CRDB{} - query, rowScanner := prepareColumns(crdb, tt.args.columns) + query, rowScanner := prepareColumns(crdb, tt.args.columns, tt.args.useV1) if query != tt.res.query { t.Errorf("prepareColumns() got = %s, want %s", query, tt.res.query) } @@ -184,8 +238,13 @@ func Test_prepareColumns(t *testing.T) { if tt.res.dbErr != nil && tt.res.dbErr(err) { return } - if !reflect.DeepEqual(reflect.Indirect(reflect.ValueOf(tt.args.dest)).Interface(), tt.res.expected) { - t.Errorf("unexpected result from rowScanner \nwant: %+v \ngot: %+v", tt.fields.dbRow, reflect.Indirect(reflect.ValueOf(tt.args.dest)).Interface()) + if equalizer, ok := tt.res.expected.(interface{ Equal(time.Time) bool }); ok { + equalizer.Equal(tt.args.dest.(*sql.NullTime).Time) + return + } + got := reflect.Indirect(reflect.ValueOf(tt.args.dest)).Interface() + if !reflect.DeepEqual(got, tt.res.expected) { + t.Errorf("unexpected result from rowScanner \nwant: %+v \ngot: %+v", tt.res.expected, got) } }) } @@ -200,6 +259,13 @@ func prepareTestScan(err error, res []interface{}) scan { return errors.ThrowInvalidArgumentf(nil, "SQL-NML1q", "expected len %d got %d", len(res), len(dests)) } for i, r := range res { + _, ok := dests[i].(*eventstore.Version) + if ok { + val, ok := r.(uint8) + if ok { + r = eventstore.Version("" + strconv.Itoa(int(val))) + } + } reflect.ValueOf(dests[i]).Elem().Set(reflect.ValueOf(r)) } @@ -209,7 +275,8 @@ func prepareTestScan(err error, res []interface{}) scan { func Test_prepareCondition(t *testing.T) { type args struct { - filters [][]*repository.Filter + query *repository.SearchQuery + useV1 bool } type res struct { clause string @@ -223,7 +290,18 @@ func Test_prepareCondition(t *testing.T) { { name: "nil filters", args: args{ - filters: nil, + query: &repository.SearchQuery{}, + useV1: true, + }, + res: res{ + clause: "", + values: nil, + }, + }, + { + name: "nil filters v2", + args: args{ + query: &repository.SearchQuery{}, }, res: res{ clause: "", @@ -233,7 +311,22 @@ func Test_prepareCondition(t *testing.T) { { name: "empty filters", args: args{ - filters: [][]*repository.Filter{}, + query: &repository.SearchQuery{ + SubQueries: [][]*repository.Filter{}, + }, + useV1: true, + }, + res: res{ + clause: "", + values: nil, + }, + }, + { + name: "empty filters v2", + args: args{ + query: &repository.SearchQuery{ + SubQueries: [][]*repository.Filter{}, + }, }, res: res{ clause: "", @@ -243,9 +336,28 @@ func Test_prepareCondition(t *testing.T) { { name: "invalid condition", args: args{ - filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateID, "wrong", repository.Operation(-1)), + query: &repository.SearchQuery{ + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateID, "wrong", repository.Operation(-1)), + }, + }, + }, + useV1: true, + }, + res: res{ + clause: "", + values: nil, + }, + }, + { + name: "invalid condition v2", + args: args{ + query: &repository.SearchQuery{ + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateID, "wrong", repository.Operation(-1)), + }, }, }, }, @@ -257,38 +369,82 @@ func Test_prepareCondition(t *testing.T) { { name: "array as condition value", args: args{ - filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, []repository.AggregateType{"user", "org"}, repository.OperationIn), + query: &repository.SearchQuery{ + AwaitOpenTransactions: true, + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"user", "org"}, repository.OperationIn), + }, + }, + }, + useV1: true, + }, + res: res{ + clause: " WHERE aggregate_type = ANY(?) AND creation_date::TIMESTAMP < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = 'zitadel_es_pusher')", + values: []interface{}{[]eventstore.AggregateType{"user", "org"}}, + }, + }, + { + name: "array as condition value v2", + args: args{ + query: &repository.SearchQuery{ + AwaitOpenTransactions: true, + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"user", "org"}, repository.OperationIn), + }, }, }, }, res: res{ - clause: " WHERE ( aggregate_type = ANY(?) )", - values: []interface{}{[]repository.AggregateType{"user", "org"}}, + clause: ` WHERE aggregate_type = ANY(?) AND hlc_to_timestamp("position") < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = 'zitadel_es_pusher')`, + values: []interface{}{[]eventstore.AggregateType{"user", "org"}}, }, }, { name: "multiple filters", args: args{ - filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, []repository.AggregateType{"user", "org"}, repository.OperationIn), - repository.NewFilter(repository.FieldAggregateID, "1234", repository.OperationEquals), - repository.NewFilter(repository.FieldEventType, []repository.EventType{"user.created", "org.created"}, repository.OperationIn), + query: &repository.SearchQuery{ + AwaitOpenTransactions: true, + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"user", "org"}, repository.OperationIn), + repository.NewFilter(repository.FieldAggregateID, "1234", repository.OperationEquals), + repository.NewFilter(repository.FieldEventType, []eventstore.EventType{"user.created", "org.created"}, repository.OperationIn), + }, + }, + }, + useV1: true, + }, + res: res{ + clause: " WHERE aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) AND creation_date::TIMESTAMP < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = 'zitadel_es_pusher')", + values: []interface{}{[]eventstore.AggregateType{"user", "org"}, "1234", []eventstore.EventType{"user.created", "org.created"}}, + }, + }, + { + name: "multiple filters v2", + args: args{ + query: &repository.SearchQuery{ + AwaitOpenTransactions: true, + SubQueries: [][]*repository.Filter{ + { + repository.NewFilter(repository.FieldAggregateType, []eventstore.AggregateType{"user", "org"}, repository.OperationIn), + repository.NewFilter(repository.FieldAggregateID, "1234", repository.OperationEquals), + repository.NewFilter(repository.FieldEventType, []eventstore.EventType{"user.created", "org.created"}, repository.OperationIn), + }, }, }, }, res: res{ - clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )", - values: []interface{}{[]repository.AggregateType{"user", "org"}, "1234", []repository.EventType{"user.created", "org.created"}}, + clause: ` WHERE aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) AND hlc_to_timestamp("position") < (SELECT COALESCE(MIN(start), NOW())::TIMESTAMP FROM crdb_internal.cluster_transactions where application_name = 'zitadel_es_pusher')`, + values: []interface{}{[]eventstore.AggregateType{"user", "org"}, "1234", []eventstore.EventType{"user.created", "org.created"}}, }, }, } + crdb := NewCRDB(&database.DB{Database: new(cockroach.Config)}) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - crdb := &CRDB{} - gotClause, gotValues := prepareCondition(crdb, tt.args.filters) + gotClause, gotValues := prepareConditions(crdb, tt.args.query, tt.args.useV1) if gotClause != tt.res.clause { t.Errorf("prepareCondition() gotClause = %v, want %v", gotClause, tt.res.clause) } @@ -307,10 +463,10 @@ func Test_prepareCondition(t *testing.T) { func Test_query_events_with_crdb(t *testing.T) { type args struct { - searchQuery *repository.SearchQuery + searchQuery *eventstore.SearchQueryBuilder } type fields struct { - existingEvents []*repository.Event + existingEvents []eventstore.Command client *sql.DB } type res struct { @@ -326,18 +482,14 @@ func Test_query_events_with_crdb(t *testing.T) { { name: "aggregate type filter no events", args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, "not found", repository.OperationEquals), - }, - }, - }, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + AggregateTypes("not found"). + Builder(), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{ + existingEvents: []eventstore.Command{ generateEvent(t, "300"), generateEvent(t, "300"), generateEvent(t, "300"), @@ -351,18 +503,14 @@ func Test_query_events_with_crdb(t *testing.T) { { name: "aggregate type filter events found", args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, t.Name(), repository.OperationEquals), - }, - }, - }, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + AggregateTypes(eventstore.AggregateType(t.Name())). + Builder(), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{ + existingEvents: []eventstore.Command{ generateEvent(t, "301"), generateEvent(t, "302"), generateEvent(t, "302"), @@ -377,19 +525,15 @@ func Test_query_events_with_crdb(t *testing.T) { { name: "aggregate type and id filter events found", args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, t.Name(), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, "303", repository.OperationEquals), - }, - }, - }, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + AggregateTypes(eventstore.AggregateType(t.Name())). + AggregateIDs("303"). + Builder(), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{ + existingEvents: []eventstore.Command{ generateEvent(t, "303"), generateEvent(t, "303"), generateEvent(t, "303"), @@ -405,18 +549,12 @@ func Test_query_events_with_crdb(t *testing.T) { { name: "resource owner filter events found", args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldResourceOwner, "caos", repository.OperationEquals), - }, - }, - }, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner("caos"), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{ + existingEvents: []eventstore.Command{ generateEvent(t, "306", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), generateEvent(t, "307", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), generateEvent(t, "308", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), @@ -429,90 +567,26 @@ func Test_query_events_with_crdb(t *testing.T) { }, wantErr: false, }, - { - name: "editor service filter events found", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldEditorService, "MANAGEMENT-API", repository.OperationEquals), - repository.NewFilter(repository.FieldEditorService, "ADMIN-API", repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - client: testCRDBClient, - existingEvents: []*repository.Event{ - generateEvent(t, "307", func(e *repository.Event) { e.EditorService = "MANAGEMENT-API" }), - generateEvent(t, "307", func(e *repository.Event) { e.EditorService = "MANAGEMENT-API" }), - generateEvent(t, "308", func(e *repository.Event) { e.EditorService = "ADMIN-API" }), - generateEvent(t, "309", func(e *repository.Event) { e.EditorService = "AUTHAPI" }), - generateEvent(t, "309", func(e *repository.Event) { e.EditorService = "AUTHAPI" }), - }, - }, - res: res{ - eventCount: 3, - }, - wantErr: false, - }, - { - name: "editor user filter events found", - args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldEditorUser, "adlerhurst", repository.OperationEquals), - repository.NewFilter(repository.FieldEditorUser, "nobody", repository.OperationEquals), - repository.NewFilter(repository.FieldEditorUser, "", repository.OperationEquals), - }, - }, - }, - }, - fields: fields{ - client: testCRDBClient, - existingEvents: []*repository.Event{ - generateEvent(t, "310", func(e *repository.Event) { e.EditorUser = "adlerhurst" }), - generateEvent(t, "310", func(e *repository.Event) { e.EditorUser = "adlerhurst" }), - generateEvent(t, "310", func(e *repository.Event) { e.EditorUser = "nobody" }), - generateEvent(t, "311", func(e *repository.Event) { e.EditorUser = "" }), - generateEvent(t, "311", func(e *repository.Event) { e.EditorUser = "" }), - generateEvent(t, "312", func(e *repository.Event) { e.EditorUser = "fforootd" }), - generateEvent(t, "312", func(e *repository.Event) { e.EditorUser = "fforootd" }), - }, - }, - res: res{ - eventCount: 5, - }, - wantErr: false, - }, { name: "event type filter events found", args: args{ - searchQuery: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldEventType, repository.EventType("user.created"), repository.OperationEquals), - repository.NewFilter(repository.FieldEventType, repository.EventType("user.updated"), repository.OperationEquals), - }, - }, - }, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AddQuery(). + EventTypes("user.created", "user.updated"). + Builder(), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{ - generateEvent(t, "311", func(e *repository.Event) { e.Type = "user.created" }), - generateEvent(t, "311", func(e *repository.Event) { e.Type = "user.updated" }), - generateEvent(t, "311", func(e *repository.Event) { e.Type = "user.deactivated" }), - generateEvent(t, "311", func(e *repository.Event) { e.Type = "user.locked" }), - generateEvent(t, "312", func(e *repository.Event) { e.Type = "user.created" }), - generateEvent(t, "312", func(e *repository.Event) { e.Type = "user.updated" }), - generateEvent(t, "312", func(e *repository.Event) { e.Type = "user.deactivated" }), - generateEvent(t, "312", func(e *repository.Event) { e.Type = "user.reactivated" }), - generateEvent(t, "313", func(e *repository.Event) { e.Type = "user.locked" }), + existingEvents: []eventstore.Command{ + generateEvent(t, "311", func(e *repository.Event) { e.Typ = "user.created" }), + generateEvent(t, "311", func(e *repository.Event) { e.Typ = "user.updated" }), + generateEvent(t, "311", func(e *repository.Event) { e.Typ = "user.deactivated" }), + generateEvent(t, "311", func(e *repository.Event) { e.Typ = "user.locked" }), + generateEvent(t, "312", func(e *repository.Event) { e.Typ = "user.created" }), + generateEvent(t, "312", func(e *repository.Event) { e.Typ = "user.updated" }), + generateEvent(t, "312", func(e *repository.Event) { e.Typ = "user.deactivated" }), + generateEvent(t, "312", func(e *repository.Event) { e.Typ = "user.reactivated" }), + generateEvent(t, "313", func(e *repository.Event) { e.Typ = "user.locked" }), }, }, res: res{ @@ -523,11 +597,11 @@ func Test_query_events_with_crdb(t *testing.T) { { name: "fail because no filter", args: args{ - searchQuery: &repository.SearchQuery{}, + searchQuery: eventstore.NewSearchQueryBuilder(eventstore.Columns(-1)), }, fields: fields{ client: testCRDBClient, - existingEvents: []*repository.Event{}, + existingEvents: []eventstore.Command{}, }, res: res{ eventCount: 0, @@ -542,17 +616,16 @@ func Test_query_events_with_crdb(t *testing.T) { DB: tt.fields.client, Database: new(testDB), }, - AllowOrderByCreationDate: true, } // setup initial data for query - if err := db.Push(context.Background(), tt.fields.existingEvents); err != nil { + if _, err := db.Push(context.Background(), tt.fields.existingEvents...); err != nil { t.Errorf("error in setup = %v", err) return } - events := []*repository.Event{} - if err := query(context.Background(), db, tt.args.searchQuery, &events); (err != nil) != tt.wantErr { + events := []eventstore.Event{} + if err := query(context.Background(), db, tt.args.searchQuery, &events, true); (err != nil) != tt.wantErr { t.Errorf("CRDB.query() error = %v, wantErr %v", err, tt.wantErr) } }) @@ -561,7 +634,7 @@ func Test_query_events_with_crdb(t *testing.T) { func Test_query_events_mocked(t *testing.T) { type args struct { - query *repository.SearchQuery + query *eventstore.SearchQueryBuilder dest interface{} } type res struct { @@ -580,24 +653,17 @@ func Test_query_events_mocked(t *testing.T) { name: "with order by desc", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, - []driver.Value{repository.AggregateType("user")}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC`, + []driver.Value{eventstore.AggregateType("user")}, ), }, res: res{ @@ -608,25 +674,18 @@ func Test_query_events_mocked(t *testing.T) { name: "with limit", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 5, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderAsc(). + AwaitOpenTransactions(). + Limit(5). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date, event_sequence LIMIT \$2`, - []driver.Value{repository.AggregateType("user"), uint64(5)}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence LIMIT \$2`, + []driver.Value{eventstore.AggregateType("user"), uint64(5)}, ), }, res: res{ @@ -637,25 +696,18 @@ func Test_query_events_mocked(t *testing.T) { name: "with limit and order by desc", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 5, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + Limit(5). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$2`, - []driver.Value{repository.AggregateType("user"), uint64(5)}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC LIMIT \$2`, + []driver.Value{eventstore.AggregateType("user"), uint64(5)}, ), }, res: res{ @@ -666,26 +718,19 @@ func Test_query_events_mocked(t *testing.T) { name: "with limit and order by desc as of system time", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 5, - AllowTimeTravel: true, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + Limit(5). + AllowTimeTravel(). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$2`, - []driver.Value{repository.AggregateType("user"), uint64(5)}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC LIMIT \$2`, + []driver.Value{eventstore.AggregateType("user"), uint64(5)}, ), }, res: res{ @@ -696,25 +741,18 @@ func Test_query_events_mocked(t *testing.T) { name: "error sql conn closed", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 0, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + Limit(0). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQueryErr(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, - []driver.Value{repository.AggregateType("user")}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC`, + []driver.Value{eventstore.AggregateType("user")}, sql.ErrConnDone), }, res: res{ @@ -725,26 +763,19 @@ func Test_query_events_mocked(t *testing.T) { name: "error unexpected dest", args: args{ dest: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 0, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + Limit(0). + AddQuery(). + AggregateTypes("user"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQueryScanErr(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, - []driver.Value{repository.AggregateType("user")}, - &repository.Event{Sequence: 100}), + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE aggregate_type = \$1 AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC`, + []driver.Value{eventstore.AggregateType("user")}, + &repository.Event{Seq: 100}), }, res: res{ wantErr: true, @@ -753,25 +784,7 @@ func Test_query_events_mocked(t *testing.T) { { name: "error no columns", args: args{ - query: &repository.SearchQuery{ - Columns: repository.Columns(-1), - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "invalid condition", - args: args{ - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Filters: [][]*repository.Filter{ - { - {}, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.Columns(-1)), }, res: res{ wantErr: true, @@ -781,37 +794,21 @@ func Test_query_events_mocked(t *testing.T) { name: "with subqueries", args: args{ dest: &[]*repository.Event{}, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 5, - Filters: [][]*repository.Filter{ - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("user"), - Operation: repository.OperationEquals, - }, - }, - { - { - Field: repository.FieldAggregateType, - Value: repository.AggregateType("org"), - Operation: repository.OperationEquals, - }, - { - Field: repository.FieldAggregateID, - Value: "asdf42", - Operation: repository.OperationEquals, - }, - }, - }, - }, + query: eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + OrderDesc(). + AwaitOpenTransactions(). + Limit(5). + AddQuery(). + AggregateTypes("user"). + Or(). + AggregateTypes("org"). + AggregateIDs("asdf42"). + Builder(), }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) OR \( aggregate_type = \$2 AND aggregate_id = \$3 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$4`, - []driver.Value{repository.AggregateType("user"), repository.AggregateType("org"), "asdf42", uint64(5)}, + `SELECT creation_date, event_type, event_sequence, event_data, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \(aggregate_type = \$1 OR \(aggregate_type = \$2 AND aggregate_id = \$3\)\) AND creation_date::TIMESTAMP < \(SELECT COALESCE\(MIN\(start\), NOW\(\)\)::TIMESTAMP FROM crdb_internal\.cluster_transactions where application_name = 'zitadel_es_pusher'\) ORDER BY event_sequence DESC LIMIT \$4`, + []driver.Value{eventstore.AggregateType("user"), eventstore.AggregateType("org"), "asdf42", uint64(5)}, ), }, res: res{ @@ -819,19 +816,14 @@ func Test_query_events_mocked(t *testing.T) { }, }, } + crdb := NewCRDB(&database.DB{Database: new(testDB)}) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - crdb := &CRDB{ - DB: &database.DB{ - Database: new(testDB), - }, - AllowOrderByCreationDate: true, - } if tt.fields.mock != nil { crdb.DB.DB = tt.fields.mock.client } - err := query(context.Background(), crdb, tt.args.query, tt.args.dest) + err := query(context.Background(), crdb, tt.args.query, tt.args.dest, true) if (err != nil) != tt.res.wantErr { t.Errorf("query() error = %v, wantErr %v", err, tt.res.wantErr) } @@ -856,9 +848,9 @@ func (m *dbMock) expectQuery(t *testing.T, expectedQuery string, args []driver.V m.mock.ExpectBegin() query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...) m.mock.ExpectCommit() - rows := sqlmock.NewRows([]string{"event_sequence"}) + rows := sqlmock.NewRows([]string{"sequence"}) for _, event := range events { - rows = rows.AddRow(event.Sequence) + rows = rows.AddRow(event.Seq) } query.WillReturnRows(rows).RowsWillBeClosed() return m @@ -868,9 +860,9 @@ func (m *dbMock) expectQueryScanErr(t *testing.T, expectedQuery string, args []d m.mock.ExpectBegin() query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...) m.mock.ExpectRollback() - rows := sqlmock.NewRows([]string{"event_sequence"}) + rows := sqlmock.NewRows([]string{"sequence"}) for _, event := range events { - rows = rows.AddRow(event.Sequence) + rows = rows.AddRow(event.Seq) } query.WillReturnRows(rows).RowsWillBeClosed() return m diff --git a/internal/eventstore/repository/sql/types.go b/internal/eventstore/repository/sql/types.go deleted file mode 100644 index 78ed8f7a6..000000000 --- a/internal/eventstore/repository/sql/types.go +++ /dev/null @@ -1,49 +0,0 @@ -package sql - -import ( - "database/sql/driver" -) - -// Data represents a byte array that may be null. -// Data implements the sql.Scanner interface -type Data []byte - -// Scan implements the Scanner interface. -func (data *Data) Scan(value interface{}) error { - if value == nil { - *data = nil - return nil - } - *data = Data(value.([]byte)) - return nil -} - -// Value implements the driver Valuer interface. -func (data Data) Value() (driver.Value, error) { - if len(data) == 0 { - return nil, nil - } - return []byte(data), nil -} - -// Sequence represents a number that may be null. -// Sequence implements the sql.Scanner interface -type Sequence uint64 - -// Scan implements the Scanner interface. -func (seq *Sequence) Scan(value interface{}) error { - if value == nil { - *seq = 0 - return nil - } - *seq = Sequence(value.(int64)) - return nil -} - -// Value implements the driver Valuer interface. -func (seq Sequence) Value() (driver.Value, error) { - if seq == 0 { - return nil, nil - } - return int64(seq), nil -} diff --git a/internal/eventstore/repository/unique_constraint.go b/internal/eventstore/repository/unique_constraint.go deleted file mode 100644 index 96d05d1e2..000000000 --- a/internal/eventstore/repository/unique_constraint.go +++ /dev/null @@ -1,33 +0,0 @@ -package repository - -// UniqueCheck represents all information about a unique attribute -type UniqueConstraint struct { - //UniqueField is the field which should be unique - UniqueField string - - //UniqueType is the type of the unique field - UniqueType string - - //InstanceID represents the instance - InstanceID string - - //Action defines if unique constraint should be added or removed - Action UniqueConstraintAction - - //ErrorMessage is the message key which should be returned if constraint is violated - ErrorMessage string -} - -type UniqueConstraintAction int32 - -const ( - UniqueConstraintAdd UniqueConstraintAction = iota - UniqueConstraintRemoved - UniqueConstraintInstanceRemoved - - uniqueConstraintActionCount -) - -func (f UniqueConstraintAction) Valid() bool { - return f >= 0 && f < uniqueConstraintActionCount -} diff --git a/internal/eventstore/repository/version.go b/internal/eventstore/repository/version.go deleted file mode 100644 index c534f9a41..000000000 --- a/internal/eventstore/repository/version.go +++ /dev/null @@ -1,20 +0,0 @@ -package repository - -import ( - "regexp" - - "github.com/zitadel/zitadel/internal/errors" -) - -var versionRegexp = regexp.MustCompile(`^v[0-9]+(\.[0-9]+){0,2}$`) - -//Version represents the semver of an aggregate -type Version string - -//Validate checks if the v is semver -func (v Version) Validate() error { - if !versionRegexp.MatchString(string(v)) { - return errors.ThrowPreconditionFailed(nil, "MODEL-luDuS", "version is not semver") - } - return nil -} diff --git a/internal/eventstore/search_query.go b/internal/eventstore/search_query.go index 9acf33ea4..3cdd7f4d8 100644 --- a/internal/eventstore/search_query.go +++ b/internal/eventstore/search_query.go @@ -4,61 +4,134 @@ import ( "database/sql" "time" - "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) // SearchQueryBuilder represents the builder for your filter // if invalid data are set the filter will fail type SearchQueryBuilder struct { - columns repository.Columns - limit uint64 - desc bool - resourceOwner string - instanceID string - editorUser string - queries []*SearchQuery - tx *sql.Tx - allowTimeTravel bool + columns Columns + limit uint64 + desc bool + resourceOwner string + instanceID *string + excludedInstanceIDs []string + editorUser string + queries []*SearchQuery + tx *sql.Tx + allowTimeTravel bool + positionAfter float64 + awaitOpenTransactions bool + creationDateAfter time.Time + eventSequenceGreater uint64 +} + +func (b *SearchQueryBuilder) GetColumns() Columns { + return b.columns +} + +func (b *SearchQueryBuilder) GetLimit() uint64 { + return b.limit +} + +func (b *SearchQueryBuilder) GetDesc() bool { + return b.desc +} + +func (b *SearchQueryBuilder) GetResourceOwner() string { + return b.resourceOwner +} + +func (b *SearchQueryBuilder) GetInstanceID() *string { + return b.instanceID +} + +func (b *SearchQueryBuilder) GetEditorUser() string { + return b.editorUser +} + +func (b *SearchQueryBuilder) GetQueries() []*SearchQuery { + return b.queries +} + +func (b *SearchQueryBuilder) GetTx() *sql.Tx { + return b.tx +} + +func (b *SearchQueryBuilder) GetAllowTimeTravel() bool { + return b.allowTimeTravel +} + +func (b SearchQueryBuilder) GetPositionAfter() float64 { + return b.positionAfter +} + +func (b SearchQueryBuilder) GetAwaitOpenTransactions() bool { + return b.awaitOpenTransactions +} + +func (q SearchQueryBuilder) GetExcludedInstanceIDs() []string { + return q.excludedInstanceIDs +} + +func (q SearchQueryBuilder) GetEventSequenceGreater() uint64 { + return q.eventSequenceGreater +} + +func (q SearchQueryBuilder) GetCreationDateAfter() time.Time { + return q.creationDateAfter } type SearchQuery struct { - builder *SearchQueryBuilder - aggregateTypes []AggregateType - aggregateIDs []string - instanceID string - excludedInstanceIDs []string - eventSequenceGreater uint64 - eventSequenceLess uint64 - eventTypes []EventType - eventData map[string]interface{} - creationDateAfter time.Time + builder *SearchQueryBuilder + aggregateTypes []AggregateType + aggregateIDs []string + eventTypes []EventType + eventData map[string]interface{} +} + +func (q SearchQuery) GetAggregateTypes() []AggregateType { + return q.aggregateTypes +} + +func (q SearchQuery) GetAggregateIDs() []string { + return q.aggregateIDs +} + +func (q SearchQuery) GetEventTypes() []EventType { + return q.eventTypes +} + +func (q SearchQuery) GetEventData() map[string]interface{} { + return q.eventData } // Columns defines which fields of the event are needed for the query -type Columns repository.Columns +type Columns int8 const ( //ColumnsEvent represents all fields of an event - ColumnsEvent Columns = repository.ColumnsEvent + ColumnsEvent = iota + 1 // ColumnsMaxSequence represents the latest sequence of the filtered events - ColumnsMaxSequence Columns = repository.ColumnsMaxSequence + ColumnsMaxSequence // ColumnsInstanceIDs represents the instance ids of the filtered events - ColumnsInstanceIDs Columns = repository.ColumnsInstanceIDs + ColumnsInstanceIDs + + columnsCount ) -// AggregateType is the object name -type AggregateType repository.AggregateType - -// EventType is the description of the change -type EventType repository.EventType +func (c Columns) Validate() error { + if c <= 0 || c >= columnsCount { + return errors.ThrowPreconditionFailed(nil, "REPOS-x8R35", "column out of range") + } + return nil +} // NewSearchQueryBuilder creates a new builder for event filters // aggregateTypes must contain at least one aggregate type func NewSearchQueryBuilder(columns Columns) *SearchQueryBuilder { return &SearchQueryBuilder{ - columns: repository.Columns(columns), + columns: columns, } } @@ -69,7 +142,10 @@ func (builder *SearchQueryBuilder) Matches(event Event, existingLen int) (matche if builder.resourceOwner != "" && event.Aggregate().ResourceOwner != builder.resourceOwner { return false } - if event.Aggregate().InstanceID != "" && builder.instanceID != "" && event.Aggregate().InstanceID != builder.instanceID { + if event.Aggregate().InstanceID != "" && builder.instanceID != nil && *builder.instanceID != "" && event.Aggregate().InstanceID != *builder.instanceID { + return false + } + if builder.eventSequenceGreater > 0 && event.Sequence() <= builder.eventSequenceGreater { return false } @@ -86,7 +162,7 @@ func (builder *SearchQueryBuilder) Matches(event Event, existingLen int) (matche // Columns defines which fields are set func (builder *SearchQueryBuilder) Columns(columns Columns) *SearchQueryBuilder { - builder.columns = repository.Columns(columns) + builder.columns = columns return builder } @@ -104,7 +180,7 @@ func (builder *SearchQueryBuilder) ResourceOwner(resourceOwner string) *SearchQu // InstanceID defines the instanceID (system) of the events func (builder *SearchQueryBuilder) InstanceID(instanceID string) *SearchQueryBuilder { - builder.instanceID = instanceID + builder.instanceID = &instanceID return builder } @@ -138,6 +214,39 @@ func (builder *SearchQueryBuilder) AllowTimeTravel() *SearchQueryBuilder { return builder } +// PositionAfter filters for events which happened after the specified time +func (builder *SearchQueryBuilder) PositionAfter(position float64) *SearchQueryBuilder { + builder.positionAfter = position + return builder +} + +// AwaitOpenTransactions filters for events which are older than the oldest transaction of the database +func (builder *SearchQueryBuilder) AwaitOpenTransactions() *SearchQueryBuilder { + builder.awaitOpenTransactions = true + return builder +} + +// SequenceGreater filters for events with sequence greater the requested sequence +func (builder *SearchQueryBuilder) SequenceGreater(sequence uint64) *SearchQueryBuilder { + builder.eventSequenceGreater = sequence + return builder +} + +// ExcludedInstanceID filters for events not having the given instanceIDs +func (builder *SearchQueryBuilder) ExcludedInstanceID(instanceIDs ...string) *SearchQueryBuilder { + builder.excludedInstanceIDs = instanceIDs + return builder +} + +// CreationDateAfter filters for events which happened after the specified time +func (builder *SearchQueryBuilder) CreationDateAfter(creationDate time.Time) *SearchQueryBuilder { + if creationDate.IsZero() || creationDate.Unix() == 0 { + return builder + } + builder.creationDateAfter = creationDate + return builder +} + // AddQuery creates a new sub query. // All fields in the sub query are AND-connected in the storage request. // Multiple sub queries are OR-connected in the storage request. @@ -161,42 +270,12 @@ func (query *SearchQuery) AggregateTypes(types ...AggregateType) *SearchQuery { return query } -// SequenceGreater filters for events with sequence greater the requested sequence -func (query *SearchQuery) SequenceGreater(sequence uint64) *SearchQuery { - query.eventSequenceGreater = sequence - return query -} - -// SequenceLess filters for events with sequence less the requested sequence -func (query *SearchQuery) SequenceLess(sequence uint64) *SearchQuery { - query.eventSequenceLess = sequence - return query -} - // AggregateIDs filters for events with the given aggregate id's func (query *SearchQuery) AggregateIDs(ids ...string) *SearchQuery { query.aggregateIDs = ids return query } -// InstanceID filters for events with the given instanceID -func (query *SearchQuery) InstanceID(instanceID string) *SearchQuery { - query.instanceID = instanceID - return query -} - -// ExcludedInstanceID filters for events not having the given instanceIDs -func (query *SearchQuery) ExcludedInstanceID(instanceIDs ...string) *SearchQuery { - query.excludedInstanceIDs = instanceIDs - return query -} - -// CreationDateNewer filters for events which happened after the specified time -func (query *SearchQuery) CreationDateAfter(time time.Time) *SearchQuery { - query.creationDateAfter = time - return query -} - // EventTypes filters for events with the given event types func (query *SearchQuery) EventTypes(types ...EventType) *SearchQuery { query.eventTypes = types @@ -216,176 +295,14 @@ func (query *SearchQuery) Builder() *SearchQueryBuilder { } func (query *SearchQuery) matches(event Event) bool { - if query.eventSequenceLess > 0 && event.Sequence() >= query.eventSequenceLess { - return false - } - if query.eventSequenceGreater > 0 && event.Sequence() <= query.eventSequenceGreater { - return false - } if ok := isAggreagteTypes(event.Aggregate(), query.aggregateTypes...); len(query.aggregateTypes) > 0 && !ok { return false } if ok := isAggregateIDs(event.Aggregate(), query.aggregateIDs...); len(query.aggregateIDs) > 0 && !ok { return false } - if event.Aggregate().InstanceID != "" && query.instanceID != "" && event.Aggregate().InstanceID != query.instanceID { - return false - } if ok := isEventTypes(event, query.eventTypes...); len(query.eventTypes) > 0 && !ok { return false } return true } - -func (builder *SearchQueryBuilder) build(instanceID string) (*repository.SearchQuery, error) { - if builder == nil || - len(builder.queries) < 1 || - builder.columns.Validate() != nil { - return nil, errors.ThrowPreconditionFailed(nil, "MODEL-4m9gs", "builder invalid") - } - builder.instanceID = instanceID - filters := make([][]*repository.Filter, len(builder.queries)) - - for i, query := range builder.queries { - for _, f := range []func() *repository.Filter{ - query.aggregateTypeFilter, - query.aggregateIDFilter, - query.eventTypeFilter, - query.eventDataFilter, - query.eventSequenceGreaterFilter, - query.eventSequenceLessFilter, - query.instanceIDFilter, - query.excludedInstanceIDFilter, - query.creationDateAfterFilter, - query.builder.resourceOwnerFilter, - query.builder.instanceIDFilter, - query.builder.editorUserFilter, - } { - if filter := f(); filter != nil { - if err := filter.Validate(); err != nil { - return nil, err - } - filters[i] = append(filters[i], filter) - } - } - - } - - return &repository.SearchQuery{ - Columns: builder.columns, - Limit: builder.limit, - Desc: builder.desc, - Filters: filters, - Tx: builder.tx, - AllowTimeTravel: builder.allowTimeTravel, - }, nil -} - -func (query *SearchQuery) aggregateIDFilter() *repository.Filter { - if len(query.aggregateIDs) < 1 { - return nil - } - if len(query.aggregateIDs) == 1 { - return repository.NewFilter(repository.FieldAggregateID, query.aggregateIDs[0], repository.OperationEquals) - } - return repository.NewFilter(repository.FieldAggregateID, database.StringArray(query.aggregateIDs), repository.OperationIn) -} - -func (query *SearchQuery) eventTypeFilter() *repository.Filter { - if len(query.eventTypes) < 1 { - return nil - } - if len(query.eventTypes) == 1 { - return repository.NewFilter(repository.FieldEventType, repository.EventType(query.eventTypes[0]), repository.OperationEquals) - } - eventTypes := make(database.StringArray, len(query.eventTypes)) - for i, eventType := range query.eventTypes { - eventTypes[i] = string(eventType) - } - return repository.NewFilter(repository.FieldEventType, eventTypes, repository.OperationIn) -} - -func (query *SearchQuery) aggregateTypeFilter() *repository.Filter { - if len(query.aggregateTypes) < 1 { - return nil - } - if len(query.aggregateTypes) == 1 { - return repository.NewFilter(repository.FieldAggregateType, repository.AggregateType(query.aggregateTypes[0]), repository.OperationEquals) - } - aggregateTypes := make(database.StringArray, len(query.aggregateTypes)) - for i, aggregateType := range query.aggregateTypes { - aggregateTypes[i] = string(aggregateType) - } - return repository.NewFilter(repository.FieldAggregateType, aggregateTypes, repository.OperationIn) -} - -func (query *SearchQuery) eventSequenceGreaterFilter() *repository.Filter { - if query.eventSequenceGreater == 0 { - return nil - } - sortOrder := repository.OperationGreater - if query.builder.desc { - sortOrder = repository.OperationLess - } - return repository.NewFilter(repository.FieldSequence, query.eventSequenceGreater, sortOrder) -} - -func (query *SearchQuery) eventSequenceLessFilter() *repository.Filter { - if query.eventSequenceLess == 0 { - return nil - } - sortOrder := repository.OperationLess - if query.builder.desc { - sortOrder = repository.OperationGreater - } - return repository.NewFilter(repository.FieldSequence, query.eventSequenceLess, sortOrder) -} - -func (query *SearchQuery) instanceIDFilter() *repository.Filter { - if query.instanceID == "" { - return nil - } - return repository.NewFilter(repository.FieldInstanceID, query.instanceID, repository.OperationEquals) -} - -func (query *SearchQuery) excludedInstanceIDFilter() *repository.Filter { - if len(query.excludedInstanceIDs) == 0 { - return nil - } - return repository.NewFilter(repository.FieldInstanceID, database.StringArray(query.excludedInstanceIDs), repository.OperationNotIn) -} - -func (builder *SearchQueryBuilder) resourceOwnerFilter() *repository.Filter { - if builder.resourceOwner == "" { - return nil - } - return repository.NewFilter(repository.FieldResourceOwner, builder.resourceOwner, repository.OperationEquals) -} - -func (builder *SearchQueryBuilder) instanceIDFilter() *repository.Filter { - if builder.instanceID == "" { - return nil - } - return repository.NewFilter(repository.FieldInstanceID, builder.instanceID, repository.OperationEquals) -} - -func (builder *SearchQueryBuilder) editorUserFilter() *repository.Filter { - if builder.editorUser == "" { - return nil - } - return repository.NewFilter(repository.FieldEditorUser, builder.editorUser, repository.OperationEquals) -} - -func (query *SearchQuery) creationDateAfterFilter() *repository.Filter { - if query.creationDateAfter.IsZero() { - return nil - } - return repository.NewFilter(repository.FieldCreationDate, query.creationDateAfter, repository.OperationGreater) -} - -func (query *SearchQuery) eventDataFilter() *repository.Filter { - if len(query.eventData) == 0 { - return nil - } - return repository.NewFilter(repository.FieldEventData, query.eventData, repository.OperationJSONContains) -} diff --git a/internal/eventstore/search_query_test.go b/internal/eventstore/search_query_test.go index 62153161b..a125c515b 100644 --- a/internal/eventstore/search_query_test.go +++ b/internal/eventstore/search_query_test.go @@ -1,17 +1,27 @@ package eventstore import ( - "math" "reflect" "testing" - "time" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) -func testAddQuery(queryFuncs ...func(*SearchQuery) *SearchQuery) func(*SearchQueryBuilder) *SearchQueryBuilder { +func testSetQuery(queryFuncs ...func(*SearchQueryBuilder) *SearchQueryBuilder) func(*SearchQueryBuilder) *SearchQueryBuilder { + return func(builder *SearchQueryBuilder) *SearchQueryBuilder { + for _, queryFunc := range queryFuncs { + queryFunc(builder) + } + return builder + } +} + +func testSetSequenceGreater(sequence uint64) func(*SearchQueryBuilder) *SearchQueryBuilder { + return func(builder *SearchQueryBuilder) *SearchQueryBuilder { + builder = builder.SequenceGreater(sequence) + return builder + } +} + +func testAddSubQuery(queryFuncs ...func(*SearchQuery) *SearchQuery) func(*SearchQueryBuilder) *SearchQueryBuilder { return func(builder *SearchQueryBuilder) *SearchQueryBuilder { query := builder.AddQuery() for _, queryFunc := range queryFuncs { @@ -52,20 +62,6 @@ func testSetAggregateTypes(types ...AggregateType) func(*SearchQuery) *SearchQue } } -func testSetSequenceGreater(sequence uint64) func(*SearchQuery) *SearchQuery { - return func(query *SearchQuery) *SearchQuery { - query = query.SequenceGreater(sequence) - return query - } -} - -func testSetSequenceLess(sequence uint64) func(*SearchQuery) *SearchQuery { - return func(query *SearchQuery) *SearchQuery { - query = query.SequenceLess(sequence) - return query - } -} - func testSetAggregateIDs(aggregateIDs ...string) func(*SearchQuery) *SearchQuery { return func(query *SearchQuery) *SearchQuery { query = query.AggregateIDs(aggregateIDs...) @@ -87,13 +83,6 @@ func testSetResourceOwner(resourceOwner string) func(*SearchQueryBuilder) *Searc } } -func testSetCreationDateAfter(date time.Time) func(*SearchQuery) *SearchQuery { - return func(query *SearchQuery) *SearchQuery { - query = query.CreationDateAfter(date) - return query - } -} - func testSetSortOrder(asc bool) func(*SearchQueryBuilder) *SearchQueryBuilder { return func(query *SearchQueryBuilder) *SearchQueryBuilder { if asc { @@ -121,16 +110,16 @@ func TestSearchQuerybuilderSetters(t *testing.T) { columns: ColumnsEvent, }, res: &SearchQueryBuilder{ - columns: repository.Columns(ColumnsEvent), + columns: Columns(ColumnsEvent), }, }, { name: "set columns", args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testSetColumns(repository.ColumnsMaxSequence)}, + setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testSetColumns(ColumnsMaxSequence)}, }, res: &SearchQueryBuilder{ - columns: repository.ColumnsMaxSequence, + columns: ColumnsMaxSequence, }, }, { @@ -145,33 +134,18 @@ func TestSearchQuerybuilderSetters(t *testing.T) { { name: "set sequence greater", args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddQuery(testSetSequenceGreater(90))}, - }, - res: &SearchQueryBuilder{ - queries: []*SearchQuery{ - { - eventSequenceGreater: 90, - }, + setters: []func(b *SearchQueryBuilder) *SearchQueryBuilder{ + testSetQuery(testSetSequenceGreater(90)), }, }, - }, - { - name: "set sequence less", - args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddQuery(testSetSequenceLess(90))}, - }, res: &SearchQueryBuilder{ - queries: []*SearchQuery{ - { - eventSequenceLess: 90, - }, - }, + eventSequenceGreater: 90, }, }, { name: "set aggregateIDs", args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddQuery(testSetAggregateIDs("1235", "09824"))}, + setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddSubQuery(testSetAggregateIDs("1235", "09824"))}, }, res: &SearchQueryBuilder{ queries: []*SearchQuery{ @@ -184,7 +158,7 @@ func TestSearchQuerybuilderSetters(t *testing.T) { { name: "set eventTypes", args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddQuery(testSetEventTypes("user.created", "user.updated"))}, + setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddSubQuery(testSetEventTypes("user.created", "user.updated"))}, }, res: &SearchQueryBuilder{ queries: []*SearchQuery{ @@ -206,7 +180,7 @@ func TestSearchQuerybuilderSetters(t *testing.T) { { name: "default search query", args: args{ - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddQuery(testSetAggregateTypes("user"), testSetAggregateIDs("1235", "024")), testSetSortOrder(false)}, + setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{testAddSubQuery(testSetAggregateTypes("user"), testSetAggregateIDs("1235", "024")), testSetSortOrder(false)}, }, res: &SearchQueryBuilder{ desc: true, @@ -231,496 +205,6 @@ func TestSearchQuerybuilderSetters(t *testing.T) { } } -func TestSearchQuerybuilderBuild(t *testing.T) { - testNow := time.Now() - type args struct { - columns Columns - setters []func(*SearchQueryBuilder) *SearchQueryBuilder - instanceID string - } - type res struct { - isErr func(err error) bool - query *repository.SearchQuery - } - tests := []struct { - name string - args args - res res - }{ - { - name: "no aggregate types", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{}, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - query: nil, - }, - }, - { - name: "invalid column (too low)", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetColumns(Columns(-1)), - testAddQuery(testSetAggregateTypes("user")), - }, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - }, - }, - { - name: "invalid column (too high)", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetColumns(math.MaxInt32), - testAddQuery(testSetAggregateTypes("uesr")), - }, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - }, - }, - { - name: "filter aggregate type", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery(testSetAggregateTypes("user")), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter aggregate types", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery(testSetAggregateTypes("user", "org")), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, database.StringArray{"user", "org"}, repository.OperationIn), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, desc", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetLimit(5), - testSetSortOrder(false), - testAddQuery( - testSetSequenceGreater(100), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: true, - Limit: 5, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldSequence, uint64(100), repository.OperationLess), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, asc", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetLimit(5), - testSetSortOrder(true), - testAddQuery( - testSetSequenceGreater(100), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 5, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldSequence, uint64(100), repository.OperationGreater), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, desc, max event sequence cols", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetLimit(5), - testSetSortOrder(false), - testSetColumns(repository.ColumnsMaxSequence), - testAddQuery( - testSetSequenceGreater(100), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsMaxSequence, - Desc: true, - Limit: 5, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldSequence, uint64(100), repository.OperationLess), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and aggregate id", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetAggregateIDs("1234"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, "1234", repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter multiple aggregate type and aggregate id", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetAggregateIDs("1234"), - testOr( - testSetAggregateTypes("org"), - testSetAggregateIDs("izu"), - ), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, "1234", repository.OperationEquals), - }, - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("org"), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, "izu", repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and aggregate ids", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetAggregateIDs("1234", "0815"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldAggregateID, database.StringArray{"1234", "0815"}, repository.OperationIn), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and sequence greater", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetSequenceGreater(8), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldSequence, uint64(8), repository.OperationGreater), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and event type", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetEventTypes("user.created"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldEventType, repository.EventType("user.created"), repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and event types", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetEventTypes("user.created", "user.changed"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldEventType, database.StringArray{"user.created", "user.changed"}, repository.OperationIn), - }, - }, - }, - }, - }, - { - name: "filter aggregate type resource owner", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testSetResourceOwner("hodor"), - testAddQuery( - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldResourceOwner, "hodor", repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and sequence between", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetSequenceGreater(8), - testSetSequenceLess(16), - ), - }, - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldSequence, uint64(8), repository.OperationGreater), - repository.NewFilter(repository.FieldSequence, uint64(16), repository.OperationLess), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and instanceID", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - ), - }, - instanceID: "instanceID", - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldInstanceID, "instanceID", repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, instanceID and creation date after", - args: args{ - columns: ColumnsEvent, - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - testSetCreationDateAfter(testNow), - ), - }, - instanceID: "instanceID", - }, - res: res{ - isErr: nil, - query: &repository.SearchQuery{ - Columns: repository.ColumnsEvent, - Desc: false, - Limit: 0, - Filters: [][]*repository.Filter{ - { - repository.NewFilter(repository.FieldAggregateType, repository.AggregateType("user"), repository.OperationEquals), - repository.NewFilter(repository.FieldCreationDate, testNow, repository.OperationGreater), - repository.NewFilter(repository.FieldInstanceID, "instanceID", repository.OperationEquals), - }, - }, - }, - }, - }, - { - name: "column invalid", - args: args{ - columns: Columns(-1), - setters: []func(*SearchQueryBuilder) *SearchQueryBuilder{ - testAddQuery( - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - builder := NewSearchQueryBuilder(tt.args.columns) - for _, f := range tt.args.setters { - builder = f(builder) - } - query, err := builder.build(tt.args.instanceID) - if tt.res.isErr != nil && !tt.res.isErr(err) { - t.Errorf("wrong error(%T): %v", err, err) - return - } - if err != nil && tt.res.isErr == nil { - t.Errorf("no error expected: %v", err) - return - } - - assertRepoQuery(t, tt.res.query, query) - }) - } -} - func assertBuilder(t *testing.T, want, got *SearchQueryBuilder) { t.Helper() @@ -757,62 +241,14 @@ func assertQuery(t *testing.T, i int, want, got *SearchQuery) { if !reflect.DeepEqual(got.eventData, want.eventData) { t.Errorf("wrong eventData in query %d : got: %v want: %v", i, got.eventData, want.eventData) } - if got.eventSequenceLess != want.eventSequenceLess { - t.Errorf("wrong eventSequenceLess in query %d : got: %v want: %v", i, got.eventSequenceLess, want.eventSequenceLess) - } - if got.eventSequenceGreater != want.eventSequenceGreater { - t.Errorf("wrong eventSequenceGreater in query %d : got: %v want: %v", i, got.eventSequenceGreater, want.eventSequenceGreater) - } + // if got.eventSequenceGreater != want.eventSequenceGreater { + // t.Errorf("wrong eventSequenceGreater in query %d : got: %v want: %v", i, got.eventSequenceGreater, want.eventSequenceGreater) + // } if !reflect.DeepEqual(got.eventTypes, want.eventTypes) { t.Errorf("wrong eventTypes in query %d : got: %v want: %v", i, got.eventTypes, want.eventTypes) } } -func assertRepoQuery(t *testing.T, want, got *repository.SearchQuery) { - t.Helper() - - if want == nil && got == nil { - return - } - - if !reflect.DeepEqual(got.Columns, want.Columns) { - t.Errorf("wrong columns in query: got: %v want: %v", got.Columns, want.Columns) - } - if got.Desc != want.Desc { - t.Errorf("wrong desc in query: got: %v want: %v", got.Desc, want.Desc) - } - if got.Limit != want.Limit { - t.Errorf("wrong limit in query: got: %v want: %v", got.Limit, want.Limit) - } - - if len(got.Filters) != len(want.Filters) { - t.Errorf("wrong length of filters: got: %v want: %v", len(got.Filters), len(want.Filters)) - } - - for filterIdx, filter := range got.Filters { - if len(got.Filters) != len(want.Filters) { - t.Errorf("wrong length of subfilters: got: %v want: %v", len(filter), len(want.Filters[filterIdx])) - } - for subFilterIdx, f := range filter { - assertFilters(t, subFilterIdx, want.Filters[filterIdx][subFilterIdx], f) - } - } -} - -func assertFilters(t *testing.T, i int, want, got *repository.Filter) { - t.Helper() - - if want.Field != got.Field { - t.Errorf("wrong field in filter %d : got: %v want: %v", i, got.Field, want.Field) - } - if want.Operation != got.Operation { - t.Errorf("wrong operation in filter %d : got: %v want: %v", i, got.Operation, want.Operation) - } - if !reflect.DeepEqual(want.Value, got.Value) { - t.Errorf("wrong value in filter %d : got: %v want: %v", i, got.Value, want.Value) - } -} - func TestSearchQuery_matches(t *testing.T) { type args struct { event Event @@ -823,27 +259,11 @@ func TestSearchQuery_matches(t *testing.T) { event Event want bool }{ - { - name: "sequence too low", - query: NewSearchQueryBuilder(ColumnsEvent).AddQuery().SequenceLess(10), - event: &BaseEvent{ - sequence: 10, - }, - want: false, - }, - { - name: "sequence too high", - query: NewSearchQueryBuilder(ColumnsEvent).AddQuery().SequenceGreater(60), - event: &BaseEvent{ - sequence: 60, - }, - want: false, - }, { name: "wrong aggregate type", query: NewSearchQueryBuilder(ColumnsEvent).AddQuery().AggregateTypes("searched"), event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ Type: "found", }, }, @@ -853,7 +273,7 @@ func TestSearchQuery_matches(t *testing.T) { name: "wrong aggregate id", query: NewSearchQueryBuilder(ColumnsEvent).AddQuery().AggregateIDs("1", "10", "100"), event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ ID: "2", }, }, @@ -864,16 +284,20 @@ func TestSearchQuery_matches(t *testing.T) { query: NewSearchQueryBuilder(ColumnsEvent).AddQuery().EventTypes("event.searched.type"), event: &BaseEvent{ EventType: "event.actual.type", + Agg: &Aggregate{}, }, want: false, }, { name: "matching", - query: NewSearchQueryBuilder(ColumnsEvent).AddQuery(). - SequenceLess(100).SequenceGreater(50).AggregateIDs("2").AggregateTypes("actual").EventTypes("event.actual.type"), + query: NewSearchQueryBuilder(ColumnsEvent). + AddQuery(). + AggregateIDs("2"). + AggregateTypes("actual"). + EventTypes("event.actual.type"), event: &BaseEvent{ - sequence: 55, - aggregate: Aggregate{ + Seq: 55, + Agg: &Aggregate{ ID: "2", Type: "actual", }, @@ -885,8 +309,8 @@ func TestSearchQuery_matches(t *testing.T) { name: "matching empty query", query: NewSearchQueryBuilder(ColumnsEvent).AddQuery(), event: &BaseEvent{ - sequence: 55, - aggregate: Aggregate{ + Seq: 55, + Agg: &Aggregate{ ID: "2", Type: "actual", }, @@ -898,12 +322,10 @@ func TestSearchQuery_matches(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { query := &SearchQuery{ - aggregateTypes: tt.query.aggregateTypes, - aggregateIDs: tt.query.aggregateIDs, - eventSequenceGreater: tt.query.eventSequenceGreater, - eventSequenceLess: tt.query.eventSequenceLess, - eventTypes: tt.query.eventTypes, - eventData: tt.query.eventData, + aggregateTypes: tt.query.aggregateTypes, + aggregateIDs: tt.query.aggregateIDs, + eventTypes: tt.query.eventTypes, + eventData: tt.query.eventData, } if got := query.matches(tt.event); got != tt.want { t.Errorf("SearchQuery.matches() = %v, want %v", got, tt.want) @@ -923,6 +345,19 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { args args want bool }{ + { + name: "sequence too high", + builder: NewSearchQueryBuilder(ColumnsEvent).SequenceGreater(60), + args: args{ + event: &BaseEvent{ + Agg: &Aggregate{ + InstanceID: "instance", + }, + Seq: 60, + }, + }, + want: false, + }, { name: "limit exeeded", builder: NewSearchQueryBuilder(ColumnsEvent).Limit(100), @@ -937,7 +372,7 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { builder: NewSearchQueryBuilder(ColumnsEvent).ResourceOwner("query"), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ ResourceOwner: "ro", }, }, @@ -950,7 +385,7 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { builder: NewSearchQueryBuilder(ColumnsEvent).InstanceID("instance"), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ InstanceID: "different instance", }, }, @@ -959,14 +394,12 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { want: false, }, { - name: "query failed", - builder: NewSearchQueryBuilder(ColumnsEvent). - AddQuery(). - SequenceGreater(1000). - Builder(), + name: "query failed", + builder: NewSearchQueryBuilder(ColumnsEvent).SequenceGreater(1000), args: args{ event: &BaseEvent{ - sequence: 999, + Seq: 999, + Agg: &Aggregate{}, }, existingLen: 0, }, @@ -978,16 +411,14 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { Limit(1000). ResourceOwner("ro"). InstanceID("instance"). - AddQuery(). - SequenceGreater(1000). - Builder(), + SequenceGreater(1000), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ ResourceOwner: "ro", InstanceID: "instance", }, - sequence: 1001, + Seq: 1001, }, existingLen: 999, }, @@ -998,11 +429,11 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { builder: NewSearchQueryBuilder(ColumnsEvent), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ ResourceOwner: "ro", InstanceID: "instance", }, - sequence: 1001, + Seq: 1001, }, existingLen: 999, }, @@ -1013,10 +444,10 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { builder: NewSearchQueryBuilder(ColumnsEvent), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ ResourceOwner: "ro", }, - sequence: 1001, + Seq: 1001, }, existingLen: 999, }, @@ -1027,10 +458,10 @@ func TestSearchQueryBuilder_Matches(t *testing.T) { builder: NewSearchQueryBuilder(ColumnsEvent), args: args{ event: &BaseEvent{ - aggregate: Aggregate{ + Agg: &Aggregate{ InstanceID: "instance", }, - sequence: 1001, + Seq: 1001, }, existingLen: 999, }, diff --git a/internal/eventstore/subscription.go b/internal/eventstore/subscription.go index 5348d89f2..c76c81df1 100644 --- a/internal/eventstore/subscription.go +++ b/internal/eventstore/subscription.go @@ -3,8 +3,7 @@ package eventstore import ( "sync" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/logging" ) var ( @@ -17,7 +16,7 @@ type Subscription struct { types map[AggregateType][]EventType } -//SubscribeAggregates subscribes for all events on the given aggregates +// SubscribeAggregates subscribes for all events on the given aggregates func SubscribeAggregates(eventQueue chan Event, aggregates ...AggregateType) *Subscription { types := make(map[AggregateType][]EventType, len(aggregates)) for _, aggregate := range aggregates { @@ -38,10 +37,9 @@ func SubscribeAggregates(eventQueue chan Event, aggregates ...AggregateType) *Su return sub } -//SubscribeEventTypes subscribes for the given event types +// SubscribeEventTypes subscribes for the given event types // if no event types are provided the subscription is for all events of the aggregate func SubscribeEventTypes(eventQueue chan Event, types map[AggregateType][]EventType) *Subscription { - aggregates := make([]AggregateType, len(types)) sub := &Subscription{ Events: eventQueue, types: types, @@ -50,15 +48,14 @@ func SubscribeEventTypes(eventQueue chan Event, types map[AggregateType][]EventT subsMutext.Lock() defer subsMutext.Unlock() - for _, aggregate := range aggregates { + for aggregate := range types { subscriptions[aggregate] = append(subscriptions[aggregate], sub) } return sub } -func notify(events []Event) { - go v1.Notify(MapEventsToV1Events(events)) +func (es *Eventstore) notify(events []Event) { subsMutext.Lock() defer subsMutext.Unlock() for _, event := range events { @@ -76,7 +73,11 @@ func notify(events []Event) { //subscription for certain events for _, eventType := range eventTypes { if event.Type() == eventType { - sub.Events <- event + select { + case sub.Events <- event: + default: + logging.Debug("unable to push event") + } break } } @@ -105,26 +106,3 @@ func (s *Subscription) Unsubscribe() { close(s.Events) } } - -func MapEventsToV1Events(events []Event) []*models.Event { - v1Events := make([]*models.Event, len(events)) - for i, event := range events { - v1Events[i] = mapEventToV1Event(event) - } - return v1Events -} - -func mapEventToV1Event(event Event) *models.Event { - return &models.Event{ - Sequence: event.Sequence(), - CreationDate: event.CreationDate(), - Type: models.EventType(event.Type()), - AggregateType: models.AggregateType(event.Aggregate().Type), - AggregateID: event.Aggregate().ID, - ResourceOwner: event.Aggregate().ResourceOwner, - InstanceID: event.Aggregate().InstanceID, - EditorService: event.EditorService(), - EditorUser: event.EditorUser(), - Data: event.DataAsBytes(), - } -} diff --git a/internal/eventstore/unique_constraint.go b/internal/eventstore/unique_constraint.go index 4c329c456..4486e19e5 100644 --- a/internal/eventstore/unique_constraint.go +++ b/internal/eventstore/unique_constraint.go @@ -1,31 +1,37 @@ package eventstore -type EventUniqueConstraint struct { +type UniqueConstraint struct { // UniqueType is the table name for the unique constraint UniqueType string - //UniqueField is the unique key + // UniqueField is the unique key UniqueField string - //Action defines if unique constraint should be added or removed + // Action defines if unique constraint should be added or removed Action UniqueConstraintAction - //ErrorMessage defines the translation file key for the error message + // ErrorMessage defines the translation file key for the error message ErrorMessage string - //IsGlobal defines if the unique constraint is globally unique or just within a single instance + // IsGlobal defines if the unique constraint is globally unique or just within a single instance IsGlobal bool } -type UniqueConstraintAction int32 +type UniqueConstraintAction int8 const ( UniqueConstraintAdd UniqueConstraintAction = iota UniqueConstraintRemove UniqueConstraintInstanceRemove + + uniqueConstraintActionCount ) +func (f UniqueConstraintAction) Valid() bool { + return f >= 0 && f < uniqueConstraintActionCount +} + func NewAddEventUniqueConstraint( uniqueType, uniqueField, - errMessage string) *EventUniqueConstraint { - return &EventUniqueConstraint{ + errMessage string) *UniqueConstraint { + return &UniqueConstraint{ UniqueType: uniqueType, UniqueField: uniqueField, ErrorMessage: errMessage, @@ -33,27 +39,27 @@ func NewAddEventUniqueConstraint( } } -func NewRemoveEventUniqueConstraint( +func NewRemoveUniqueConstraint( uniqueType, - uniqueField string) *EventUniqueConstraint { - return &EventUniqueConstraint{ + uniqueField string) *UniqueConstraint { + return &UniqueConstraint{ UniqueType: uniqueType, UniqueField: uniqueField, Action: UniqueConstraintRemove, } } -func NewRemoveInstanceUniqueConstraints() *EventUniqueConstraint { - return &EventUniqueConstraint{ +func NewRemoveInstanceUniqueConstraints() *UniqueConstraint { + return &UniqueConstraint{ Action: UniqueConstraintInstanceRemove, } } -func NewAddGlobalEventUniqueConstraint( +func NewAddGlobalUniqueConstraint( uniqueType, uniqueField, - errMessage string) *EventUniqueConstraint { - return &EventUniqueConstraint{ + errMessage string) *UniqueConstraint { + return &UniqueConstraint{ UniqueType: uniqueType, UniqueField: uniqueField, ErrorMessage: errMessage, @@ -62,10 +68,10 @@ func NewAddGlobalEventUniqueConstraint( } } -func NewRemoveGlobalEventUniqueConstraint( +func NewRemoveGlobalUniqueConstraint( uniqueType, - uniqueField string) *EventUniqueConstraint { - return &EventUniqueConstraint{ + uniqueField string) *UniqueConstraint { + return &UniqueConstraint{ UniqueType: uniqueType, UniqueField: uniqueField, IsGlobal: true, diff --git a/internal/eventstore/v1/eventstore.go b/internal/eventstore/v1/eventstore.go deleted file mode 100644 index 5c350d7fe..000000000 --- a/internal/eventstore/v1/eventstore.go +++ /dev/null @@ -1,51 +0,0 @@ -package v1 - -import ( - "context" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore/v1/internal/repository" - z_sql "github.com/zitadel/zitadel/internal/eventstore/v1/internal/repository/sql" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/telemetry/tracing" -) - -type Eventstore interface { - Health(ctx context.Context) error - FilterEvents(ctx context.Context, searchQuery *models.SearchQuery) (events []*models.Event, err error) - Subscribe(aggregates ...models.AggregateType) *Subscription - InstanceIDs(ctx context.Context, searchQuery *models.SearchQuery) ([]string, error) -} - -var _ Eventstore = (*eventstore)(nil) - -type eventstore struct { - repo repository.Repository -} - -func Start(db *database.DB, allowOrderByCreationDate bool) (Eventstore, error) { - return &eventstore{ - repo: z_sql.Start(db, allowOrderByCreationDate), - }, nil -} - -func (es *eventstore) FilterEvents(ctx context.Context, searchQuery *models.SearchQuery) (_ []*models.Event, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - if err := searchQuery.Validate(); err != nil { - return nil, err - } - return es.repo.Filter(ctx, models.FactoryFromSearchQuery(searchQuery)) -} - -func (es *eventstore) Health(ctx context.Context) error { - return es.repo.Health(ctx) -} - -func (es *eventstore) InstanceIDs(ctx context.Context, searchQuery *models.SearchQuery) ([]string, error) { - if err := searchQuery.Validate(); err != nil { - return nil, err - } - return es.repo.InstanceIDs(ctx, models.FactoryFromSearchQuery(searchQuery)) -} diff --git a/internal/eventstore/v1/gen_mock.go b/internal/eventstore/v1/gen_mock.go deleted file mode 100644 index 7c86dd52f..000000000 --- a/internal/eventstore/v1/gen_mock.go +++ /dev/null @@ -1,3 +0,0 @@ -package v1 - -//go:generate mockgen -package mock -destination ./mock/eventstore.mock.go github.com/zitadel/zitadel/internal/eventstore Eventstore diff --git a/internal/eventstore/v1/internal/repository/gen_mock.go b/internal/eventstore/v1/internal/repository/gen_mock.go deleted file mode 100644 index 2500cfe8b..000000000 --- a/internal/eventstore/v1/internal/repository/gen_mock.go +++ /dev/null @@ -1,3 +0,0 @@ -package repository - -//go:generate mockgen -package mock -destination ./mock/repository.mock.go github.com/zitadel/zitadel/internal/eventstore/internal/repository Repository diff --git a/internal/eventstore/v1/internal/repository/mock/repository.go b/internal/eventstore/v1/internal/repository/mock/repository.go deleted file mode 100644 index 8574eb300..000000000 --- a/internal/eventstore/v1/internal/repository/mock/repository.go +++ /dev/null @@ -1,34 +0,0 @@ -package mock - -import ( - "context" - "testing" - - gomock "github.com/golang/mock/gomock" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -func NewMock(t *testing.T) *MockRepository { - return NewMockRepository(gomock.NewController(t)) -} - -func (m *MockRepository) ExpectFilter(query *models.SearchQuery, eventAmount int) *MockRepository { - events := make([]*models.Event, eventAmount) - m.EXPECT().Filter(context.Background(), query).Return(events, nil).MaxTimes(1) - return m -} - -func (m *MockRepository) ExpectFilterFail(query *models.SearchQuery, err error) *MockRepository { - m.EXPECT().Filter(context.Background(), query).Return(nil, err).MaxTimes(1) - return m -} - -func (m *MockRepository) ExpectPush(aggregates ...*models.Aggregate) *MockRepository { - m.EXPECT().PushAggregates(context.Background(), aggregates).Return(nil).MaxTimes(1) - return m -} - -func (m *MockRepository) ExpectPushError(err error, aggregates ...*models.Aggregate) *MockRepository { - m.EXPECT().PushAggregates(context.Background(), aggregates).Return(err).MaxTimes(1) - return m -} diff --git a/internal/eventstore/v1/internal/repository/mock/repository.mock.go b/internal/eventstore/v1/internal/repository/mock/repository.mock.go deleted file mode 100644 index 0e5bd031c..000000000 --- a/internal/eventstore/v1/internal/repository/mock/repository.mock.go +++ /dev/null @@ -1,98 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/zitadel/zitadel/internal/eventstore/internal/repository (interfaces: Repository) - -// Package mock is a generated GoMock package. -package mock - -import ( - context "context" - models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - gomock "github.com/golang/mock/gomock" - reflect "reflect" -) - -// MockRepository is a mock of Repository interface -type MockRepository struct { - ctrl *gomock.Controller - recorder *MockRepositoryMockRecorder -} - -// MockRepositoryMockRecorder is the mock recorder for MockRepository -type MockRepositoryMockRecorder struct { - mock *MockRepository -} - -// NewMockRepository creates a new mock instance -func NewMockRepository(ctrl *gomock.Controller) *MockRepository { - mock := &MockRepository{ctrl: ctrl} - mock.recorder = &MockRepositoryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { - return m.recorder -} - -// Filter mocks base method -func (m *MockRepository) Filter(arg0 context.Context, arg1 *models.SearchQueryFactory) ([]*models.Event, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Filter", arg0, arg1) - ret0, _ := ret[0].([]*models.Event) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Filter indicates an expected call of Filter -func (mr *MockRepositoryMockRecorder) Filter(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockRepository)(nil).Filter), arg0, arg1) -} - -// Health mocks base method -func (m *MockRepository) Health(arg0 context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Health", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Health indicates an expected call of Health -func (mr *MockRepositoryMockRecorder) Health(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockRepository)(nil).Health), arg0) -} - -// LatestSequence mocks base method -func (m *MockRepository) LatestSequence(arg0 context.Context, arg1 *models.SearchQueryFactory) (uint64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LatestSequence", arg0, arg1) - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LatestSequence indicates an expected call of LatestSequence -func (mr *MockRepositoryMockRecorder) LatestSequence(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSequence", reflect.TypeOf((*MockRepository)(nil).LatestSequence), arg0, arg1) -} - -// PushAggregates mocks base method -func (m *MockRepository) PushAggregates(arg0 context.Context, arg1 ...*models.Aggregate) error { - m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "PushAggregates", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// PushAggregates indicates an expected call of PushAggregates -func (mr *MockRepositoryMockRecorder) PushAggregates(arg0 interface{}, arg1 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushAggregates", reflect.TypeOf((*MockRepository)(nil).PushAggregates), varargs...) -} diff --git a/internal/eventstore/v1/internal/repository/repository.go b/internal/eventstore/v1/internal/repository/repository.go deleted file mode 100644 index 8f7d1e469..000000000 --- a/internal/eventstore/v1/internal/repository/repository.go +++ /dev/null @@ -1,18 +0,0 @@ -package repository - -import ( - "context" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -type Repository interface { - Health(ctx context.Context) error - - // Filter returns all events matching the given search query - Filter(ctx context.Context, searchQuery *models.SearchQueryFactory) (events []*models.Event, err error) - //LatestSequence returns the latest sequence found by the search query - LatestSequence(ctx context.Context, queryFactory *models.SearchQueryFactory) (uint64, error) - //InstanceIDs returns the instance ids found by the search query - InstanceIDs(ctx context.Context, queryFactory *models.SearchQueryFactory) ([]string, error) -} diff --git a/internal/eventstore/v1/internal/repository/sql/config.go b/internal/eventstore/v1/internal/repository/sql/config.go deleted file mode 100644 index 5ea34e6e4..000000000 --- a/internal/eventstore/v1/internal/repository/sql/config.go +++ /dev/null @@ -1,12 +0,0 @@ -package sql - -import ( - "github.com/zitadel/zitadel/internal/database" -) - -func Start(client *database.DB, allowOrderByCreationDate bool) *SQL { - return &SQL{ - client: client, - allowOrderByCreationDate: allowOrderByCreationDate, - } -} diff --git a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go b/internal/eventstore/v1/internal/repository/sql/db_mock_test.go deleted file mode 100644 index da64dfd5d..000000000 --- a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go +++ /dev/null @@ -1,208 +0,0 @@ -package sql - -import ( - "database/sql" - "regexp" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -const ( - selectEscaped = `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore\.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1` -) - -var ( - eventColumns = []string{"creation_date", "event_type", "event_sequence", "previous_aggregate_sequence", "event_data", "editor_service", "editor_user", "resource_owner", "instance_id", "aggregate_type", "aggregate_id", "aggregate_version"} - expectedFilterEventsLimitFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date, event_sequence LIMIT \$2`).String() - expectedFilterEventsDescFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date DESC, event_sequence DESC`).String() - expectedFilterEventsAggregateIDLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY creation_date, event_sequence LIMIT \$3`).String() - expectedFilterEventsAggregateIDTypeLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY creation_date, event_sequence LIMIT \$3`).String() - expectedGetAllEvents = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date, event_sequence`).String() - - expectedInsertStatement = regexp.MustCompile(`INSERT INTO eventstore\.events ` + - `\(event_type, aggregate_type, aggregate_id, aggregate_version, creation_date, event_data, editor_user, editor_service, resource_owner, instance_id, previous_aggregate_sequence, previous_aggregate_type_sequence\) ` + - `SELECT \$1, \$2, \$3, \$4, COALESCE\(\$5, now\(\)\), \$6, \$7, \$8, \$9, \$10, \$11 ` + - `WHERE EXISTS \(` + - `SELECT 1 FROM eventstore\.events WHERE aggregate_type = \$12 AND aggregate_id = \$13 HAVING MAX\(event_sequence\) = \$14 OR \(\$14::BIGINT IS NULL AND COUNT\(\*\) = 0\)\) ` + - `RETURNING event_sequence, creation_date`).String() -) - -type dbMock struct { - sqlClient *sql.DB - mock sqlmock.Sqlmock -} - -func (db *dbMock) close() { - db.sqlClient.Close() -} - -func mockDB(t *testing.T) *dbMock { - mockDB := dbMock{} - var err error - mockDB.sqlClient, mockDB.mock, err = sqlmock.New() - if err != nil { - t.Fatalf("error occured while creating stub db %v", err) - } - - mockDB.mock.MatchExpectationsInOrder(true) - - return &mockDB -} - -func (db *dbMock) expectBegin(err error) *dbMock { - if err != nil { - db.mock.ExpectBegin().WillReturnError(err) - } else { - db.mock.ExpectBegin() - } - return db -} - -func (db *dbMock) expectSavepoint() *dbMock { - db.mock.ExpectExec("SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1)) - - return db -} - -func (db *dbMock) expectReleaseSavepoint(err error) *dbMock { - expectation := db.mock.ExpectExec("RELEASE SAVEPOINT") - if err == nil { - expectation.WillReturnResult(sqlmock.NewResult(1, 1)) - } else { - expectation.WillReturnError(err) - } - - return db -} - -func (db *dbMock) expectCommit(err error) *dbMock { - if err != nil { - db.mock.ExpectCommit().WillReturnError(err) - } else { - db.mock.ExpectCommit() - } - return db -} - -func (db *dbMock) expectRollback(err error) *dbMock { - if err != nil { - db.mock.ExpectRollback().WillReturnError(err) - } else { - db.mock.ExpectRollback() - } - return db -} - -func (db *dbMock) expectInsertEvent(e *models.Event, returnedSequence uint64) *dbMock { - db.mock.ExpectQuery(expectedInsertStatement). - WithArgs( - e.Type, e.AggregateType, e.AggregateID, e.AggregateVersion, sqlmock.AnyArg(), Data(e.Data), e.EditorUser, e.EditorService, e.ResourceOwner, e.InstanceID, Sequence(e.PreviousSequence), - e.AggregateType, e.AggregateID, Sequence(e.PreviousSequence), Sequence(e.PreviousSequence), - ). - WillReturnRows( - sqlmock.NewRows([]string{"event_sequence", "creation_date"}). - AddRow(returnedSequence, time.Now().UTC()), - ) - - return db -} - -func (db *dbMock) expectInsertEventError(e *models.Event) *dbMock { - db.mock.ExpectQuery(expectedInsertStatement). - WithArgs( - e.Type, e.AggregateType, e.AggregateID, e.AggregateVersion, sqlmock.AnyArg(), Data(e.Data), e.EditorUser, e.EditorService, e.ResourceOwner, e.InstanceID, Sequence(e.PreviousSequence), - e.AggregateType, e.AggregateID, Sequence(e.PreviousSequence), Sequence(e.PreviousSequence), - ). - WillReturnError(sql.ErrTxDone) - - return db -} - -func (db *dbMock) expectFilterEventsLimit(aggregateType string, limit uint64, eventCount int) *dbMock { - rows := sqlmock.NewRows(eventColumns) - for i := 0; i < eventCount; i++ { - rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") - } - db.mock.ExpectBegin() - db.mock.ExpectQuery(expectedFilterEventsLimitFormat). - WithArgs(aggregateType, limit). - WillReturnRows(rows) - db.mock.ExpectCommit() - return db -} - -func (db *dbMock) expectFilterEventsDesc(aggregateType string, eventCount int) *dbMock { - rows := sqlmock.NewRows(eventColumns) - for i := eventCount; i > 0; i-- { - rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") - } - db.mock.ExpectBegin() - db.mock.ExpectQuery(expectedFilterEventsDescFormat). - WillReturnRows(rows) - db.mock.ExpectCommit() - return db -} - -func (db *dbMock) expectFilterEventsAggregateIDLimit(aggregateType, aggregateID string, limit uint64) *dbMock { - rows := sqlmock.NewRows(eventColumns) - for i := limit; i > 0; i-- { - rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") - } - db.mock.ExpectBegin() - db.mock.ExpectQuery(expectedFilterEventsAggregateIDLimit). - WithArgs(aggregateType, aggregateID, limit). - WillReturnRows(rows) - db.mock.ExpectCommit() - return db -} - -func (db *dbMock) expectFilterEventsAggregateIDTypeLimit(aggregateType, aggregateID string, limit uint64) *dbMock { - rows := sqlmock.NewRows(eventColumns) - for i := limit; i > 0; i-- { - rows.AddRow(time.Now(), "eventType", Sequence(i+1), Sequence(i), nil, "svc", "hodor", "org", "instanceID", "aggType", "aggID", "v1.0.0") - } - db.mock.ExpectBegin() - db.mock.ExpectQuery(expectedFilterEventsAggregateIDTypeLimit). - WithArgs(aggregateType, aggregateID, limit). - WillReturnRows(rows) - db.mock.ExpectCommit() - return db -} - -func (db *dbMock) expectFilterEventsError(returnedErr error) *dbMock { - db.mock.ExpectBegin() - db.mock.ExpectQuery(expectedGetAllEvents). - WillReturnError(returnedErr) - db.mock.ExpectRollback() - return db -} - -func (db *dbMock) expectLatestSequenceFilter(aggregateType string, sequence Sequence) *dbMock { - db.mock.ExpectBegin() - db.mock.ExpectQuery(`SELECT MAX\(event_sequence\) FROM eventstore\.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \)`). - WithArgs(aggregateType). - WillReturnRows(sqlmock.NewRows([]string{"max_sequence"}).AddRow(sequence)) - db.mock.ExpectCommit() - return db -} - -func (db *dbMock) expectLatestSequenceFilterError(aggregateType string, err error) *dbMock { - db.mock.ExpectBegin() - db.mock.ExpectQuery(`SELECT MAX\(event_sequence\) FROM eventstore\.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \)`). - WithArgs(aggregateType).WillReturnError(err) - // db.mock.ExpectRollback() - return db -} - -func (db *dbMock) expectPrepareInsert(err error) *dbMock { - prepare := db.mock.ExpectPrepare(expectedInsertStatement) - if err != nil { - prepare.WillReturnError(err) - } - - return db -} diff --git a/internal/eventstore/v1/internal/repository/sql/filter.go b/internal/eventstore/v1/internal/repository/sql/filter.go deleted file mode 100644 index 850a1ee30..000000000 --- a/internal/eventstore/v1/internal/repository/sql/filter.go +++ /dev/null @@ -1,99 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "errors" - "runtime/debug" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/database" - errs "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/telemetry/tracing" -) - -type Querier interface { - Query(query string, args ...interface{}) (*sql.Rows, error) -} - -func (db *SQL) Filter(ctx context.Context, searchQuery *es_models.SearchQueryFactory) (events []*es_models.Event, err error) { - if !searchQuery.InstanceFiltered { - logging.WithFields("stack", string(debug.Stack())).Warn("instanceid not filtered") - } - return db.filter(ctx, db.client, searchQuery) -} - -func (server *SQL) filter(ctx context.Context, db *database.DB, searchQuery *es_models.SearchQueryFactory) (events []*es_models.Event, err error) { - query, limit, values, rowScanner := server.buildQuery(ctx, db, searchQuery) - if query == "" { - return nil, errs.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") - } - - events = make([]*es_models.Event, 0, limit) - err = db.QueryContext(ctx, - func(rows *sql.Rows) error { - for rows.Next() { - event := new(es_models.Event) - err := rowScanner(rows.Scan, event) - if err != nil { - return err - } - - events = append(events, event) - } - return nil - }, - query, values..., - ) - if err != nil { - logging.New().WithError(err).Info("query failed") - return nil, errs.ThrowInternal(err, "SQL-IJuyR", "unable to filter events") - } - return events, nil -} - -func (db *SQL) LatestSequence(ctx context.Context, queryFactory *es_models.SearchQueryFactory) (uint64, error) { - query, _, values, rowScanner := db.buildQuery(ctx, db.client, queryFactory) - if query == "" { - return 0, errs.ThrowInvalidArgument(nil, "SQL-rWeBw", "invalid query factory") - } - sequence := new(Sequence) - err := db.client.QueryRowContext(ctx, func(row *sql.Row) error { - return rowScanner(row.Scan, sequence) - }, query, values...) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - logging.New().WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Info("query failed") - return 0, errs.ThrowInternal(err, "SQL-Yczyx", "unable to filter latest sequence") - } - return uint64(*sequence), nil -} - -func (db *SQL) InstanceIDs(ctx context.Context, queryFactory *es_models.SearchQueryFactory) (ids []string, err error) { - query, _, values, rowScanner := db.buildQuery(ctx, db.client, queryFactory) - if query == "" { - return nil, errs.ThrowInvalidArgument(nil, "SQL-Sfwg2", "invalid query factory") - } - - err = db.client.QueryContext(ctx, - func(rows *sql.Rows) error { - for rows.Next() { - var id string - err := rowScanner(rows.Scan, &id) - if err != nil { - return err - } - - ids = append(ids, id) - } - return nil - }, - query, values...) - if err != nil { - logging.New().WithError(err).Info("query failed") - return nil, errs.ThrowInternal(err, "SQL-Sfg3r", "unable to filter instance ids") - } - - return ids, nil -} diff --git a/internal/eventstore/v1/internal/repository/sql/filter_test.go b/internal/eventstore/v1/internal/repository/sql/filter_test.go deleted file mode 100644 index 39ebbd0ad..000000000 --- a/internal/eventstore/v1/internal/repository/sql/filter_test.go +++ /dev/null @@ -1,243 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "math" - "testing" - - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -type mockEvents struct { - events []*es_models.Event - t *testing.T -} - -func TestSQL_Filter(t *testing.T) { - type fields struct { - client *dbMock - } - type args struct { - events *mockEvents - searchQuery *es_models.SearchQueryFactory - } - type res struct { - wantErr bool - isErrFunc func(error) bool - eventsLen int - } - tests := []struct { - name string - fields fields - args args - res res - }{ - { - name: "only limit filter", - fields: fields{ - client: mockDB(t).expectFilterEventsLimit("user", 34, 3), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().Limit(34).AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - eventsLen: 3, - wantErr: false, - }, - }, - { - name: "only desc filter", - fields: fields{ - client: mockDB(t).expectFilterEventsDesc("user", 34), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().OrderDesc().AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - eventsLen: 34, - wantErr: false, - }, - }, - { - name: "no events found", - fields: fields{ - client: mockDB(t).expectFilterEventsError(sql.ErrNoRows), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().AddQuery().AggregateTypes("nonAggregate").Factory(), - }, - res: res{ - wantErr: true, - isErrFunc: errors.IsInternal, - }, - }, - { - name: "filter fails because sql internal error", - fields: fields{ - client: mockDB(t).expectFilterEventsError(sql.ErrConnDone), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - wantErr: true, - isErrFunc: errors.IsInternal, - }, - }, - { - name: "filter by aggregate id", - fields: fields{ - client: mockDB(t).expectFilterEventsAggregateIDLimit("user", "hop", 5), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().Limit(5).AddQuery().AggregateTypes("user").AggregateIDs("hop").Factory(), - }, - res: res{ - wantErr: false, - isErrFunc: nil, - }, - }, - { - name: "filter by aggregate id and aggregate type", - fields: fields{ - client: mockDB(t).expectFilterEventsAggregateIDTypeLimit("user", "hop", 5), - }, - args: args{ - events: &mockEvents{t: t}, - searchQuery: es_models.NewSearchQueryFactory().Limit(5).AddQuery().AggregateTypes("user").AggregateIDs("hop").Factory(), - }, - res: res{ - wantErr: false, - isErrFunc: nil, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sql := &SQL{ - client: &database.DB{DB: tt.fields.client.sqlClient, Database: new(testDB)}, - allowOrderByCreationDate: true, - } - events, err := sql.Filter(context.Background(), tt.args.searchQuery) - if (err != nil) != tt.res.wantErr { - t.Errorf("SQL.Filter() error = %v, wantErr %v", err, tt.res.wantErr) - } - - if tt.res.eventsLen != 0 && len(events) != tt.res.eventsLen { - t.Errorf("events has wrong length got: %d want %d", len(events), tt.res.eventsLen) - } - if tt.res.wantErr && !tt.res.isErrFunc(err) { - t.Errorf("got wrong error %v", err) - } - if err := tt.fields.client.mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } - tt.fields.client.close() - }) - } -} - -func TestSQL_LatestSequence(t *testing.T) { - type fields struct { - client *dbMock - } - type args struct { - searchQuery *es_models.SearchQueryFactory - } - type res struct { - wantErr bool - isErrFunc func(error) bool - sequence uint64 - } - tests := []struct { - name string - fields fields - args args - res res - }{ - { - name: "invalid query factory", - args: args{ - searchQuery: nil, - }, - fields: fields{ - client: mockDB(t), - }, - res: res{ - wantErr: true, - isErrFunc: errors.IsErrorInvalidArgument, - }, - }, - { - name: "no events for aggregate", - args: args{ - searchQuery: es_models.NewSearchQueryFactory().Columns(es_models.Columns_Max_Sequence).AddQuery().AggregateTypes("idiot").Factory(), - }, - fields: fields{ - client: mockDB(t).expectLatestSequenceFilterError("idiot", sql.ErrNoRows), - }, - res: res{ - wantErr: false, - sequence: 0, - }, - }, - { - name: "sql query error", - args: args{ - searchQuery: es_models.NewSearchQueryFactory().Columns(es_models.Columns_Max_Sequence).AddQuery().AggregateTypes("idiot").Factory(), - }, - fields: fields{ - client: mockDB(t).expectLatestSequenceFilterError("idiot", sql.ErrConnDone), - }, - res: res{ - wantErr: true, - isErrFunc: errors.IsInternal, - sequence: 0, - }, - }, - { - name: "events for aggregate found", - args: args{ - searchQuery: es_models.NewSearchQueryFactory().Columns(es_models.Columns_Max_Sequence).AddQuery().AggregateTypes("user").Factory(), - }, - fields: fields{ - client: mockDB(t).expectLatestSequenceFilter("user", math.MaxUint64), - }, - res: res{ - wantErr: false, - sequence: math.MaxUint64, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sql := &SQL{ - client: &database.DB{DB: tt.fields.client.sqlClient, Database: new(testDB)}, - } - - sequence, err := sql.LatestSequence(context.Background(), tt.args.searchQuery) - if (err != nil) != tt.res.wantErr { - t.Errorf("SQL.Filter() error = %v, wantErr %v", err, tt.res.wantErr) - } - - if tt.res.sequence != sequence { - t.Errorf("events has wrong length got: %d want %d", sequence, tt.res.sequence) - } - if tt.res.wantErr && !tt.res.isErrFunc(err) { - t.Errorf("got wrong error %v", err) - } - if err := tt.fields.client.mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } - tt.fields.client.close() - }) - } -} diff --git a/internal/eventstore/v1/internal/repository/sql/query.go b/internal/eventstore/v1/internal/repository/sql/query.go deleted file mode 100644 index 8b2ea1dc9..000000000 --- a/internal/eventstore/v1/internal/repository/sql/query.go +++ /dev/null @@ -1,238 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strconv" - "strings" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/api/call" - "github.com/zitadel/zitadel/internal/database/dialect" - z_errors "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -const ( - selectStmt = "SELECT" + - " creation_date" + - ", event_type" + - ", event_sequence" + - ", previous_aggregate_sequence" + - ", event_data" + - ", editor_service" + - ", editor_user" + - ", resource_owner" + - ", instance_id" + - ", aggregate_type" + - ", aggregate_id" + - ", aggregate_version" + - " FROM eventstore.events" -) - -func (sql *SQL) buildQuery(ctx context.Context, db dialect.Database, queryFactory *es_models.SearchQueryFactory) (query string, limit uint64, values []interface{}, rowScanner func(s scan, dest interface{}) error) { - searchQuery, err := queryFactory.Build() - if err != nil { - logging.New().WithError(err).Warn("search query factory invalid") - return "", 0, nil, nil - } - query, rowScanner = prepareColumns(searchQuery.Columns) - where, values := prepareCondition(searchQuery.Filters) - if where == "" || query == "" { - return "", 0, nil, nil - } - - if travel := db.Timetravel(call.Took(ctx)); travel != "" { - query += travel - } - query += where - - if searchQuery.Columns == es_models.Columns_Event { - var order string - if sql.allowOrderByCreationDate { - order = " ORDER BY creation_date, event_sequence" - if searchQuery.Desc { - order = " ORDER BY creation_date DESC, event_sequence DESC" - } - } else { - order = " ORDER BY event_sequence" - if searchQuery.Desc { - order = " ORDER BY event_sequence DESC" - } - } - query += order - } - - if searchQuery.Limit > 0 { - values = append(values, searchQuery.Limit) - query += " LIMIT ?" - } - - query = numberPlaceholder(query, "?", "$") - - return query, searchQuery.Limit, values, rowScanner -} - -func prepareCondition(filters [][]*es_models.Filter) (clause string, values []interface{}) { - values = make([]interface{}, 0, len(filters)) - clauses := make([]string, len(filters)) - - if len(filters) == 0 { - return clause, values - } - for i, filter := range filters { - subClauses := make([]string, 0, len(filter)) - for _, f := range filter { - value := f.GetValue() - - subClauses = append(subClauses, getCondition(f)) - if subClauses[len(subClauses)-1] == "" { - return "", nil - } - values = append(values, value) - } - clauses[i] = "( " + strings.Join(subClauses, " AND ") + " )" - } - return " WHERE " + strings.Join(clauses, " OR "), values -} - -type scan func(dest ...interface{}) error - -func prepareColumns(columns es_models.Columns) (string, func(s scan, dest interface{}) error) { - switch columns { - case es_models.Columns_Max_Sequence: - return "SELECT MAX(event_sequence) FROM eventstore.events", func(row scan, dest interface{}) (err error) { - sequence, ok := dest.(*Sequence) - if !ok { - return z_errors.ThrowInvalidArgument(nil, "SQL-NBjA9", "type must be sequence") - } - err = row(sequence) - if err == nil || errors.Is(err, sql.ErrNoRows) { - return nil - } - return z_errors.ThrowInternal(err, "SQL-bN5xg", "something went wrong") - } - case es_models.Columns_InstanceIDs: - return "SELECT DISTINCT instance_id FROM eventstore.events", func(row scan, dest interface{}) (err error) { - instanceID, ok := dest.(*string) - if !ok { - return z_errors.ThrowInvalidArgument(nil, "SQL-Fef5h", "type must be *string]") - } - err = row(instanceID) - if err != nil { - logging.New().WithError(err).Warn("unable to scan row") - return z_errors.ThrowInternal(err, "SQL-SFef3", "unable to scan row") - } - return nil - } - case es_models.Columns_Event: - return selectStmt, func(row scan, dest interface{}) (err error) { - event, ok := dest.(*es_models.Event) - if !ok { - return z_errors.ThrowInvalidArgument(nil, "SQL-4GP6F", "type must be event") - } - var previousSequence Sequence - data := make(Data, 0) - - err = row( - &event.CreationDate, - &event.Type, - &event.Sequence, - &previousSequence, - &data, - &event.EditorService, - &event.EditorUser, - &event.ResourceOwner, - &event.InstanceID, - &event.AggregateType, - &event.AggregateID, - &event.AggregateVersion, - ) - - if err != nil { - logging.New().WithError(err).Warn("unable to scan row") - return z_errors.ThrowInternal(err, "SQL-J0hFS", "unable to scan row") - } - - event.PreviousSequence = uint64(previousSequence) - - event.Data = make([]byte, len(data)) - copy(event.Data, data) - - return nil - } - default: - return "", nil - } -} - -func numberPlaceholder(query, old, new string) string { - for i, hasChanged := 1, true; hasChanged; i++ { - newQuery := strings.Replace(query, old, new+strconv.Itoa(i), 1) - hasChanged = query != newQuery - query = newQuery - } - return query -} - -func getCondition(filter *es_models.Filter) (condition string) { - field := getField(filter.GetField()) - operation := getOperation(filter.GetOperation()) - if field == "" || operation == "" { - return "" - } - format := getConditionFormat(filter.GetOperation()) - - return fmt.Sprintf(format, field, operation) -} - -func getConditionFormat(operation es_models.Operation) string { - switch operation { - case es_models.Operation_In: - return "%s %s ANY(?)" - case es_models.Operation_NotIn: - return "%s %s ALL(?)" - } - return "%s %s ?" -} - -func getField(field es_models.Field) string { - switch field { - case es_models.Field_AggregateID: - return "aggregate_id" - case es_models.Field_AggregateType: - return "aggregate_type" - case es_models.Field_LatestSequence: - return "event_sequence" - case es_models.Field_ResourceOwner: - return "resource_owner" - case es_models.Field_InstanceID: - return "instance_id" - case es_models.Field_EditorService: - return "editor_service" - case es_models.Field_EditorUser: - return "editor_user" - case es_models.Field_EventType: - return "event_type" - case es_models.Field_CreationDate: - return "creation_date" - } - return "" -} - -func getOperation(operation es_models.Operation) string { - switch operation { - case es_models.Operation_Equals, es_models.Operation_In: - return "=" - case es_models.Operation_Greater: - return ">" - case es_models.Operation_Less: - return "<" - case es_models.Operation_NotIn: - return "<>" - } - return "" -} diff --git a/internal/eventstore/v1/internal/repository/sql/query_test.go b/internal/eventstore/v1/internal/repository/sql/query_test.go deleted file mode 100644 index 0c4b4d9db..000000000 --- a/internal/eventstore/v1/internal/repository/sql/query_test.go +++ /dev/null @@ -1,504 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "reflect" - "testing" - "time" - - "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -func Test_numberPlaceholder(t *testing.T) { - type args struct { - query string - old string - new string - } - type res struct { - query string - } - tests := []struct { - name string - args args - res res - }{ - { - name: "no replaces", - args: args{ - new: "$", - old: "?", - query: "SELECT * FROM eventstore.events", - }, - res: res{ - query: "SELECT * FROM eventstore.events", - }, - }, - { - name: "two replaces", - args: args{ - new: "$", - old: "?", - query: "SELECT * FROM eventstore.events WHERE aggregate_type = ? AND LIMIT = ?", - }, - res: res{ - query: "SELECT * FROM eventstore.events WHERE aggregate_type = $1 AND LIMIT = $2", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := numberPlaceholder(tt.args.query, tt.args.old, tt.args.new); got != tt.res.query { - t.Errorf("numberPlaceholder() = %v, want %v", got, tt.res.query) - } - }) - } -} - -func Test_getOperation(t *testing.T) { - t.Run("all ops", func(t *testing.T) { - for op, expected := range map[es_models.Operation]string{ - es_models.Operation_Equals: "=", - es_models.Operation_In: "=", - es_models.Operation_Greater: ">", - es_models.Operation_Less: "<", - es_models.Operation(-1): "", - } { - if got := getOperation(op); got != expected { - t.Errorf("getOperation() = %v, want %v", got, expected) - } - } - }) -} - -func Test_getField(t *testing.T) { - t.Run("all fields", func(t *testing.T) { - for field, expected := range map[es_models.Field]string{ - es_models.Field_AggregateType: "aggregate_type", - es_models.Field_AggregateID: "aggregate_id", - es_models.Field_LatestSequence: "event_sequence", - es_models.Field_ResourceOwner: "resource_owner", - es_models.Field_InstanceID: "instance_id", - es_models.Field_EditorService: "editor_service", - es_models.Field_EditorUser: "editor_user", - es_models.Field_EventType: "event_type", - es_models.Field(-1): "", - } { - if got := getField(field); got != expected { - t.Errorf("getField() = %v, want %v", got, expected) - } - } - }) -} - -func Test_getConditionFormat(t *testing.T) { - type args struct { - operation es_models.Operation - } - tests := []struct { - name string - args args - want string - }{ - { - name: "no in operation", - args: args{ - operation: es_models.Operation_Equals, - }, - want: "%s %s ?", - }, - { - name: "in operation", - args: args{ - operation: es_models.Operation_In, - }, - want: "%s %s ANY(?)", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := getConditionFormat(tt.args.operation); got != tt.want { - t.Errorf("prepareConditionFormat() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_getCondition(t *testing.T) { - type args struct { - filter *es_models.Filter - } - tests := []struct { - name string - args args - want string - }{ - { - name: "equals", - args: args{filter: es_models.NewFilter(es_models.Field_AggregateID, "", es_models.Operation_Equals)}, - want: "aggregate_id = ?", - }, - { - name: "greater", - args: args{filter: es_models.NewFilter(es_models.Field_LatestSequence, 0, es_models.Operation_Greater)}, - want: "event_sequence > ?", - }, - { - name: "less", - args: args{filter: es_models.NewFilter(es_models.Field_LatestSequence, 5000, es_models.Operation_Less)}, - want: "event_sequence < ?", - }, - { - name: "in list", - args: args{filter: es_models.NewFilter(es_models.Field_AggregateType, []es_models.AggregateType{"movies", "actors"}, es_models.Operation_In)}, - want: "aggregate_type = ANY(?)", - }, - { - name: "invalid operation", - args: args{filter: es_models.NewFilter(es_models.Field_AggregateType, []es_models.AggregateType{"movies", "actors"}, es_models.Operation(-1))}, - want: "", - }, - { - name: "invalid field", - args: args{filter: es_models.NewFilter(es_models.Field(-1), []es_models.AggregateType{"movies", "actors"}, es_models.Operation_Equals)}, - want: "", - }, - { - name: "invalid field and operation", - args: args{filter: es_models.NewFilter(es_models.Field(-1), []es_models.AggregateType{"movies", "actors"}, es_models.Operation(-1))}, - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := getCondition(tt.args.filter); got != tt.want { - t.Errorf("getCondition() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_prepareColumns(t *testing.T) { - type args struct { - columns es_models.Columns - dest interface{} - dbErr error - } - type res struct { - query string - dbRow []interface{} - expected interface{} - dbErr func(error) bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "invalid columns", - args: args{columns: es_models.Columns(-1)}, - res: res{ - query: "", - dbErr: func(err error) bool { return err == nil }, - }, - }, - { - name: "max column", - args: args{ - columns: es_models.Columns_Max_Sequence, - dest: new(Sequence), - }, - res: res{ - query: "SELECT MAX(event_sequence) FROM eventstore.events", - dbRow: []interface{}{Sequence(5)}, - expected: Sequence(5), - }, - }, - { - name: "max sequence wrong dest type", - args: args{ - columns: es_models.Columns_Max_Sequence, - dest: new(uint64), - }, - res: res{ - query: "SELECT MAX(event_sequence) FROM eventstore.events", - dbErr: errors.IsErrorInvalidArgument, - }, - }, - { - name: "event", - args: args{ - columns: es_models.Columns_Event, - dest: new(es_models.Event), - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", - dbRow: []interface{}{time.Time{}, es_models.EventType(""), uint64(5), Sequence(0), Data(nil), "", "", "", "", es_models.AggregateType("user"), "hodor", es_models.Version("")}, - expected: es_models.Event{AggregateID: "hodor", AggregateType: "user", Sequence: 5, Data: make(Data, 0)}, - }, - }, - { - name: "event wrong dest type", - args: args{ - columns: es_models.Columns_Event, - dest: new(uint64), - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", - dbErr: errors.IsErrorInvalidArgument, - }, - }, - { - name: "event query error", - args: args{ - columns: es_models.Columns_Event, - dest: new(es_models.Event), - dbErr: sql.ErrConnDone, - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events", - dbErr: errors.IsInternal, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - query, rowScanner := prepareColumns(tt.args.columns) - if query != tt.res.query { - t.Errorf("prepareColumns() got = %v, want %v", query, tt.res.query) - } - if tt.res.query == "" && rowScanner != nil { - t.Errorf("row scanner should be nil") - } - if rowScanner == nil { - return - } - err := rowScanner(prepareTestScan(tt.args.dbErr, tt.res.dbRow), tt.args.dest) - if tt.res.dbErr != nil { - if !tt.res.dbErr(err) { - t.Errorf("wrong error type in rowScanner got: %v", err) - } - } else { - if !reflect.DeepEqual(reflect.Indirect(reflect.ValueOf(tt.args.dest)).Interface(), tt.res.expected) { - t.Errorf("unexpected result from rowScanner want: %v got: %v", tt.res.dbRow, tt.args.dest) - } - } - }) - } -} - -func prepareTestScan(err error, res []interface{}) scan { - return func(dests ...interface{}) error { - if err != nil { - return err - } - if len(dests) != len(res) { - return errors.ThrowInvalidArgumentf(nil, "SQL-NML1q", "expected len %d got %d", len(res), len(dests)) - } - for i, r := range res { - reflect.ValueOf(dests[i]).Elem().Set(reflect.ValueOf(r)) - } - - return nil - } -} - -func Test_prepareCondition(t *testing.T) { - type args struct { - filters [][]*es_models.Filter - } - type res struct { - clause string - values []interface{} - } - tests := []struct { - name string - args args - res res - }{ - { - name: "nil filters", - args: args{ - filters: nil, - }, - res: res{ - clause: "", - values: nil, - }, - }, - { - name: "empty filters", - args: args{ - filters: [][]*es_models.Filter{}, - }, - res: res{ - clause: "", - values: nil, - }, - }, - { - name: "invalid condition", - args: args{ - filters: [][]*es_models.Filter{ - { - es_models.NewFilter(es_models.Field_AggregateID, "wrong", es_models.Operation(-1)), - }, - }, - }, - res: res{ - clause: "", - values: nil, - }, - }, - { - name: "array as condition value", - args: args{ - filters: [][]*es_models.Filter{ - { - es_models.NewFilter(es_models.Field_AggregateType, []es_models.AggregateType{"user", "org"}, es_models.Operation_In), - }, - }, - }, - res: res{ - clause: " WHERE ( aggregate_type = ANY(?) )", - values: []interface{}{[]es_models.AggregateType{"user", "org"}}, - }, - }, - { - name: "multiple filters", - args: args{ - filters: [][]*es_models.Filter{ - { - es_models.NewFilter(es_models.Field_AggregateType, []es_models.AggregateType{"user", "org"}, es_models.Operation_In), - es_models.NewFilter(es_models.Field_AggregateID, "1234", es_models.Operation_Equals), - es_models.NewFilter(es_models.Field_EventType, []es_models.EventType{"user.created", "org.created"}, es_models.Operation_In), - }, - }, - }, - res: res{ - clause: " WHERE ( aggregate_type = ANY(?) AND aggregate_id = ? AND event_type = ANY(?) )", - values: []interface{}{[]es_models.AggregateType{"user", "org"}, "1234", []es_models.EventType{"user.created", "org.created"}}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotClause, gotValues := prepareCondition(tt.args.filters) - if gotClause != tt.res.clause { - t.Errorf("prepareCondition() gotClause = %v, want %v", gotClause, tt.res.clause) - } - if len(gotValues) != len(tt.res.values) { - t.Errorf("wrong length of gotten values got = %d, want %d", len(gotValues), len(tt.res.values)) - return - } - for i, value := range gotValues { - if !reflect.DeepEqual(value, tt.res.values[i]) { - t.Errorf("prepareCondition() gotValues = %v, want %v", gotValues, tt.res.values) - } - } - }) - } -} - -func Test_buildQuery(t *testing.T) { - type args struct { - queryFactory *es_models.SearchQueryFactory - } - type res struct { - query string - limit uint64 - values []interface{} - rowScanner bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "invalid query factory", - args: args{ - queryFactory: nil, - }, - res: res{ - query: "", - limit: 0, - rowScanner: false, - values: nil, - }, - }, - { - name: "with order by desc", - args: args{ - queryFactory: es_models.NewSearchQueryFactory().OrderDesc().AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date DESC, event_sequence DESC", - rowScanner: true, - values: []interface{}{es_models.AggregateType("user")}, - }, - }, - { - name: "with limit", - args: args{ - queryFactory: es_models.NewSearchQueryFactory().Limit(5).AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date, event_sequence LIMIT $2", - rowScanner: true, - values: []interface{}{es_models.AggregateType("user"), uint64(5)}, - limit: 5, - }, - }, - { - name: "with limit and order by desc", - args: args{ - queryFactory: es_models.NewSearchQueryFactory().Limit(5).OrderDesc().AddQuery().AggregateTypes("user").Factory(), - }, - res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date DESC, event_sequence DESC LIMIT $2", - rowScanner: true, - values: []interface{}{es_models.AggregateType("user"), uint64(5)}, - limit: 5, - }, - }, - } - for _, tt := range tests { - ctx := context.Background() - db := new(testDB) - t.Run(tt.name, func(t *testing.T) { - gotQuery, gotLimit, gotValues, gotRowScanner := (&SQL{allowOrderByCreationDate: true}).buildQuery(ctx, db, tt.args.queryFactory) - if gotQuery != tt.res.query { - t.Errorf("buildQuery() gotQuery = %v, want %v", gotQuery, tt.res.query) - } - if gotLimit != tt.res.limit { - t.Errorf("buildQuery() gotLimit = %v, want %v", gotLimit, tt.res.limit) - } - if len(gotValues) != len(tt.res.values) { - t.Errorf("wrong length of gotten values got = %d, want %d", len(gotValues), len(tt.res.values)) - return - } - for i, value := range gotValues { - if !reflect.DeepEqual(value, tt.res.values[i]) { - t.Errorf("prepareCondition() gotValues = %v, want %v", gotValues, tt.res.values) - } - } - if (tt.res.rowScanner && gotRowScanner == nil) || (!tt.res.rowScanner && gotRowScanner != nil) { - t.Errorf("rowScanner should be nil==%v got nil==%v", tt.res.rowScanner, gotRowScanner == nil) - } - }) - } -} - -type testDB struct{} - -func (_ *testDB) Timetravel(time.Duration) string { return " AS OF SYSTEM TIME '-1 ms' " } - -func (*testDB) DatabaseName() string { return "db" } - -func (*testDB) Username() string { return "user" } - -func (*testDB) Type() string { return "type" } diff --git a/internal/eventstore/v1/internal/repository/sql/sql.go b/internal/eventstore/v1/internal/repository/sql/sql.go deleted file mode 100644 index ab4c9ca26..000000000 --- a/internal/eventstore/v1/internal/repository/sql/sql.go +++ /dev/null @@ -1,16 +0,0 @@ -package sql - -import ( - "context" - - "github.com/zitadel/zitadel/internal/database" -) - -type SQL struct { - client *database.DB - allowOrderByCreationDate bool -} - -func (db *SQL) Health(ctx context.Context) error { - return db.client.Ping() -} diff --git a/internal/eventstore/v1/internal/repository/sql/types.go b/internal/eventstore/v1/internal/repository/sql/types.go deleted file mode 100644 index 769bce5e9..000000000 --- a/internal/eventstore/v1/internal/repository/sql/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package sql - -import "database/sql/driver" - -// Data represents a byte array that may be null. -// Data implements the sql.Scanner interface -type Data []byte - -// Scan implements the Scanner interface. -func (data *Data) Scan(value interface{}) error { - if value == nil { - *data = nil - return nil - } - *data = Data(value.([]byte)) - return nil -} - -// Value implements the driver Valuer interface. -func (data Data) Value() (driver.Value, error) { - if len(data) == 0 { - return nil, nil - } - return []byte(data), nil -} - -// Sequence represents a number that may be null. -// Sequence implements the sql.Scanner interface -type Sequence uint64 - -// Scan implements the Scanner interface. -func (seq *Sequence) Scan(value interface{}) error { - if value == nil { - *seq = 0 - return nil - } - *seq = Sequence(value.(int64)) - return nil -} - -// Value implements the driver Valuer interface. -func (seq Sequence) Value() (driver.Value, error) { - if seq == 0 { - return nil, nil - } - return int64(seq), nil -} diff --git a/internal/eventstore/v1/locker/lock.go b/internal/eventstore/v1/locker/lock.go deleted file mode 100644 index dc0682ba5..000000000 --- a/internal/eventstore/v1/locker/lock.go +++ /dev/null @@ -1,47 +0,0 @@ -package locker - -import ( - "context" - "database/sql" - "fmt" - "time" - - "github.com/cockroachdb/cockroach-go/v2/crdb" - "github.com/zitadel/logging" - - caos_errs "github.com/zitadel/zitadel/internal/errors" -) - -const ( - insertStmtFormat = "INSERT INTO %s" + - " (locker_id, locked_until, view_name, instance_id) VALUES ($1, now()+$2::INTERVAL, $3, $4)" + - " ON CONFLICT (view_name, instance_id)" + - " DO UPDATE SET locker_id = $1, locked_until = now()+$2::INTERVAL" + - " WHERE locks.view_name = $3 AND locks.instance_id = $4 AND (locks.locker_id = $1 OR locks.locked_until < now())" - millisecondsAsSeconds = int64(time.Second / time.Millisecond) -) - -type lock struct { - LockerID string `gorm:"column:locker_id;primary_key"` - LockedUntil time.Time `gorm:"column:locked_until"` - ViewName string `gorm:"column:view_name;primary_key"` -} - -func Renew(dbClient *sql.DB, lockTable, lockerID, viewModel, instanceID string, waitTime time.Duration) error { - return crdb.ExecuteTx(context.Background(), dbClient, nil, func(tx *sql.Tx) error { - insert := fmt.Sprintf(insertStmtFormat, lockTable) - result, err := tx.Exec(insert, - lockerID, waitTime, viewModel, instanceID) - - if err != nil { - tx.Rollback() - return err - } - - if rows, _ := result.RowsAffected(); rows == 0 { - return caos_errs.ThrowAlreadyExists(nil, "SPOOL-lso0e", "view already locked") - } - logging.LogWithFields("LOCKE-lOgbg", "view", viewModel, "locker", lockerID).Debug("locker changed") - return nil - }) -} diff --git a/internal/eventstore/v1/locker/lock_test.go b/internal/eventstore/v1/locker/lock_test.go deleted file mode 100644 index 37afa65de..000000000 --- a/internal/eventstore/v1/locker/lock_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package locker - -import ( - "database/sql" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" -) - -type dbMock struct { - db *sql.DB - mock sqlmock.Sqlmock -} - -func mockDB(t *testing.T) *dbMock { - mockDB := dbMock{} - var err error - mockDB.db, mockDB.mock, err = sqlmock.New() - if err != nil { - t.Fatalf("error occured while creating stub db %v", err) - } - - mockDB.mock.MatchExpectationsInOrder(true) - - return &mockDB -} - -func (db *dbMock) expectCommit() *dbMock { - db.mock.ExpectCommit() - - return db -} - -func (db *dbMock) expectRollback() *dbMock { - db.mock.ExpectRollback() - - return db -} - -func (db *dbMock) expectBegin() *dbMock { - db.mock.ExpectBegin() - - return db -} - -func (db *dbMock) expectSavepoint() *dbMock { - db.mock.ExpectExec("SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1)) - return db -} - -func (db *dbMock) expectReleaseSavepoint() *dbMock { - db.mock.ExpectExec("RELEASE SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1)) - - return db -} - -func (db *dbMock) expectRenew(lockerID, view, instanceID string, affectedRows int64) *dbMock { - query := db.mock. - ExpectExec(`INSERT INTO table\.locks \(locker_id, locked_until, view_name, instance_id\) VALUES \(\$1, now\(\)\+\$2::INTERVAL, \$3\, \$4\) ON CONFLICT \(view_name, instance_id\) DO UPDATE SET locker_id = \$1, locked_until = now\(\)\+\$2::INTERVAL WHERE locks\.view_name = \$3 AND locks\.instance_id = \$4 AND \(locks\.locker_id = \$1 OR locks\.locked_until < now\(\)\)`). - WithArgs(lockerID, sqlmock.AnyArg(), view, instanceID). - WillReturnResult(sqlmock.NewResult(1, 1)) - - if affectedRows == 0 { - query.WillReturnResult(sqlmock.NewResult(0, 0)) - } else { - query.WillReturnResult(sqlmock.NewResult(1, affectedRows)) - } - - return db -} - -func Test_locker_Renew(t *testing.T) { - type fields struct { - db *dbMock - } - type args struct { - tableName string - lockerID string - viewModel string - instanceID string - waitTime time.Duration - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - { - name: "renew succeeded", - fields: fields{ - db: mockDB(t). - expectBegin(). - expectSavepoint(). - expectRenew("locker", "view", "instanceID", 1). - expectReleaseSavepoint(). - expectCommit(), - }, - args: args{tableName: "table.locks", lockerID: "locker", viewModel: "view", instanceID: "instanceID", waitTime: 1 * time.Second}, - wantErr: false, - }, - { - name: "renew now rows updated", - fields: fields{ - db: mockDB(t). - expectBegin(). - expectSavepoint(). - expectRenew("locker", "view", "instanceID", 0). - expectRollback(), - }, - args: args{tableName: "table.locks", lockerID: "locker", viewModel: "view", instanceID: "instanceID", waitTime: 1 * time.Second}, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := Renew(tt.fields.db.db, tt.args.tableName, tt.args.lockerID, tt.args.viewModel, tt.args.instanceID, tt.args.waitTime); (err != nil) != tt.wantErr { - t.Errorf("locker.Renew() error = %v, wantErr %v", err, tt.wantErr) - } - if err := tt.fields.db.mock.ExpectationsWereMet(); err != nil { - t.Errorf("not all database expectations met: %v", err) - } - }) - } -} diff --git a/internal/eventstore/v1/mock/eventstore.mock.go b/internal/eventstore/v1/mock/eventstore.mock.go deleted file mode 100644 index ec8ffefae..000000000 --- a/internal/eventstore/v1/mock/eventstore.mock.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/zitadel/zitadel/internal/eventstore/v1 (interfaces: Eventstore) - -// Package mock is a generated GoMock package. -package mock - -import ( - context "context" - reflect "reflect" - - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - gomock "github.com/golang/mock/gomock" -) - -// MockEventstore is a mock of Eventstore interface. -type MockEventstore struct { - ctrl *gomock.Controller - recorder *MockEventstoreMockRecorder -} - -// MockEventstoreMockRecorder is the mock recorder for MockEventstore. -type MockEventstoreMockRecorder struct { - mock *MockEventstore -} - -// NewMockEventstore creates a new mock instance. -func NewMockEventstore(ctrl *gomock.Controller) *MockEventstore { - mock := &MockEventstore{ctrl: ctrl} - mock.recorder = &MockEventstoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEventstore) EXPECT() *MockEventstoreMockRecorder { - return m.recorder -} - -// FilterEvents mocks base method. -func (m *MockEventstore) FilterEvents(arg0 context.Context, arg1 *models.SearchQuery) ([]*models.Event, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FilterEvents", arg0, arg1) - ret0, _ := ret[0].([]*models.Event) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FilterEvents indicates an expected call of FilterEvents. -func (mr *MockEventstoreMockRecorder) FilterEvents(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterEvents", reflect.TypeOf((*MockEventstore)(nil).FilterEvents), arg0, arg1) -} - -// Health mocks base method. -func (m *MockEventstore) Health(arg0 context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Health", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Health indicates an expected call of Health. -func (mr *MockEventstoreMockRecorder) Health(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockEventstore)(nil).Health), arg0) -} - -// Subscribe mocks base method. -func (m *MockEventstore) Subscribe(arg0 ...models.AggregateType) *v1.Subscription { - m.ctrl.T.Helper() - varargs := []interface{}{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Subscribe", varargs...) - ret0, _ := ret[0].(*v1.Subscription) - return ret0 -} - -// Subscribe indicates an expected call of Subscribe. -func (mr *MockEventstoreMockRecorder) Subscribe(arg0 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockEventstore)(nil).Subscribe), arg0...) -} diff --git a/internal/eventstore/v1/models/aggregate.go b/internal/eventstore/v1/models/aggregate.go deleted file mode 100644 index b996e1b8a..000000000 --- a/internal/eventstore/v1/models/aggregate.go +++ /dev/null @@ -1,99 +0,0 @@ -package models - -import ( - "time" - - "github.com/zitadel/zitadel/internal/errors" -) - -type AggregateType string - -func (at AggregateType) String() string { - return string(at) -} - -type Aggregates []*Aggregate - -type Aggregate struct { - ID string - typ AggregateType - PreviousSequence uint64 - version Version - - editorService string - editorUser string - resourceOwner string - instanceID string - Events []*Event - Precondition *precondition -} - -func (a *Aggregate) Type() AggregateType { - return a.typ -} - -type precondition struct { - Query *SearchQuery - Validation func(...*Event) error -} - -func (a *Aggregate) AppendEvent(typ EventType, payload interface{}) (*Aggregate, error) { - if string(typ) == "" { - return a, errors.ThrowInvalidArgument(nil, "MODEL-TGoCb", "no event type") - } - data, err := eventData(payload) - if err != nil { - return a, err - } - - e := &Event{ - CreationDate: time.Now(), - Data: data, - Type: typ, - AggregateID: a.ID, - AggregateType: a.typ, - AggregateVersion: a.version, - EditorService: a.editorService, - EditorUser: a.editorUser, - ResourceOwner: a.resourceOwner, - InstanceID: a.instanceID, - } - - a.Events = append(a.Events, e) - return a, nil -} - -func (a *Aggregate) SetPrecondition(query *SearchQuery, validateFunc func(...*Event) error) *Aggregate { - a.Precondition = &precondition{Query: query, Validation: validateFunc} - return a -} - -func (a *Aggregate) Validate() error { - if a == nil { - return errors.ThrowPreconditionFailed(nil, "MODEL-yi5AC", "aggregate is nil") - } - if a.ID == "" { - return errors.ThrowPreconditionFailed(nil, "MODEL-FSjKV", "id not set") - } - if string(a.typ) == "" { - return errors.ThrowPreconditionFailed(nil, "MODEL-aj4t2", "type not set") - } - if err := a.version.Validate(); err != nil { - return errors.ThrowPreconditionFailed(err, "MODEL-PupjX", "invalid version") - } - - if a.editorService == "" { - return errors.ThrowPreconditionFailed(nil, "MODEL-clYbY", "editor service not set") - } - if a.editorUser == "" { - return errors.ThrowPreconditionFailed(nil, "MODEL-Xcssi", "editor user not set") - } - if a.resourceOwner == "" { - return errors.ThrowPreconditionFailed(nil, "MODEL-eBYUW", "resource owner not set") - } - if a.Precondition != nil && (a.Precondition.Query == nil || a.Precondition.Validation == nil) { - return errors.ThrowPreconditionFailed(nil, "MODEL-EEUvA", "invalid precondition") - } - - return nil -} diff --git a/internal/eventstore/v1/models/aggregate_creator.go b/internal/eventstore/v1/models/aggregate_creator.go deleted file mode 100644 index e216eaa73..000000000 --- a/internal/eventstore/v1/models/aggregate_creator.go +++ /dev/null @@ -1,59 +0,0 @@ -package models - -import ( - "context" - - "github.com/zitadel/zitadel/internal/api/authz" -) - -type AggregateCreator struct { - serviceName string -} - -func NewAggregateCreator(serviceName string) *AggregateCreator { - return &AggregateCreator{serviceName: serviceName} -} - -type option func(*Aggregate) - -func (c *AggregateCreator) NewAggregate(ctx context.Context, id string, typ AggregateType, version Version, previousSequence uint64, opts ...option) (*Aggregate, error) { - ctxData := authz.GetCtxData(ctx) - instance := authz.GetInstance(ctx) - editorUser := ctxData.UserID - resourceOwner := ctxData.OrgID - instanceID := instance.InstanceID() - - aggregate := &Aggregate{ - ID: id, - typ: typ, - PreviousSequence: previousSequence, - version: version, - Events: make([]*Event, 0, 2), - editorService: c.serviceName, - editorUser: editorUser, - resourceOwner: resourceOwner, - instanceID: instanceID, - } - - for _, opt := range opts { - opt(aggregate) - } - - if err := aggregate.Validate(); err != nil { - return nil, err - } - - return aggregate, nil -} - -func OverwriteEditorUser(userID string) func(*Aggregate) { - return func(a *Aggregate) { - a.editorUser = userID - } -} - -func OverwriteResourceOwner(resourceOwner string) func(*Aggregate) { - return func(a *Aggregate) { - a.resourceOwner = resourceOwner - } -} diff --git a/internal/eventstore/v1/models/aggregate_creator_test.go b/internal/eventstore/v1/models/aggregate_creator_test.go deleted file mode 100644 index 114008f65..000000000 --- a/internal/eventstore/v1/models/aggregate_creator_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package models - -import ( - "context" - "reflect" - "testing" -) - -func TestAggregateCreator_NewAggregate(t *testing.T) { - type args struct { - ctx context.Context - id string - typ AggregateType - version Version - opts []option - } - tests := []struct { - name string - creator *AggregateCreator - args args - want *Aggregate - wantErr bool - }{ - { - name: "no ctxdata and no options", - creator: &AggregateCreator{serviceName: "admin"}, - wantErr: true, - want: nil, - args: args{ - ctx: context.Background(), - id: "hodor", - typ: "user", - version: "v1.0.0", - }, - }, - { - name: "no id error", - creator: &AggregateCreator{serviceName: "admin"}, - wantErr: true, - want: nil, - args: args{ - ctx: context.Background(), - typ: "user", - version: "v1.0.0", - opts: []option{ - OverwriteEditorUser("hodor"), - OverwriteResourceOwner("org"), - }, - }, - }, - { - name: "no type error", - creator: &AggregateCreator{serviceName: "admin"}, - wantErr: true, - want: nil, - args: args{ - ctx: context.Background(), - id: "hodor", - version: "v1.0.0", - opts: []option{ - OverwriteEditorUser("hodor"), - OverwriteResourceOwner("org"), - }, - }, - }, - { - name: "invalid version error", - creator: &AggregateCreator{serviceName: "admin"}, - wantErr: true, - want: nil, - args: args{ - ctx: context.Background(), - id: "hodor", - typ: "user", - opts: []option{ - OverwriteEditorUser("hodor"), - OverwriteResourceOwner("org"), - }, - }, - }, - { - name: "create ok", - creator: &AggregateCreator{serviceName: "admin"}, - wantErr: false, - want: &Aggregate{ - ID: "hodor", - Events: make([]*Event, 0, 2), - typ: "user", - version: "v1.0.0", - editorService: "admin", - editorUser: "hodor", - resourceOwner: "org", - }, - args: args{ - ctx: context.Background(), - id: "hodor", - typ: "user", - version: "v1.0.0", - opts: []option{ - OverwriteEditorUser("hodor"), - OverwriteResourceOwner("org"), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.creator.NewAggregate(tt.args.ctx, tt.args.id, tt.args.typ, tt.args.version, 0, tt.args.opts...) - if (err != nil) != tt.wantErr { - t.Errorf("AggregateCreator.NewAggregate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("AggregateCreator.NewAggregate() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/eventstore/v1/models/aggregate_test.go b/internal/eventstore/v1/models/aggregate_test.go deleted file mode 100644 index 5c2c74059..000000000 --- a/internal/eventstore/v1/models/aggregate_test.go +++ /dev/null @@ -1,310 +0,0 @@ -package models - -import ( - "testing" - - "github.com/zitadel/zitadel/internal/errors" -) - -func TestAggregate_AppendEvent(t *testing.T) { - type fields struct { - aggregate *Aggregate - } - type args struct { - typ EventType - payload interface{} - } - tests := []struct { - name string - fields fields - args args - want *Aggregate - wantErr bool - }{ - { - name: "no event type error", - fields: fields{aggregate: &Aggregate{}}, - args: args{}, - want: &Aggregate{}, - wantErr: true, - }, - { - name: "invalid payload error", - fields: fields{aggregate: &Aggregate{}}, - args: args{typ: "user", payload: 134}, - want: &Aggregate{}, - wantErr: true, - }, - { - name: "event added", - fields: fields{aggregate: &Aggregate{Events: []*Event{}}}, - args: args{typ: "user.deactivated"}, - want: &Aggregate{Events: []*Event{ - {Type: "user.deactivated"}, - }}, - wantErr: false, - }, - { - name: "event added", - fields: fields{aggregate: &Aggregate{Events: []*Event{ - {}, - }}}, - args: args{typ: "user.deactivated"}, - want: &Aggregate{Events: []*Event{ - {}, - {Type: "user.deactivated"}, - }}, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.fields.aggregate.AppendEvent(tt.args.typ, tt.args.payload) - if (err != nil) != tt.wantErr { - t.Errorf("Aggregate.AppendEvent() error = %v, wantErr %v", err, tt.wantErr) - return - } - if len(tt.fields.aggregate.Events) != len(got.Events) { - t.Errorf("events len should be %d but was %d", len(tt.fields.aggregate.Events), len(got.Events)) - } - }) - } -} - -func TestAggregate_Validate(t *testing.T) { - type fields struct { - aggregate *Aggregate - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - { - name: "aggregate nil error", - wantErr: true, - }, - { - name: "aggregate empty error", - wantErr: true, - fields: fields{aggregate: &Aggregate{}}, - }, - { - name: "no id error", - wantErr: true, - fields: fields{aggregate: &Aggregate{ - typ: "user", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Events: []*Event{ - { - AggregateType: "user", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "no type error", - wantErr: true, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "invalid version error", - wantErr: true, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - typ: "user", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateType: "user", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "no query in precondition error", - wantErr: true, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - typ: "user", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Precondition: &precondition{ - Validation: func(...*Event) error { return nil }, - }, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateType: "user", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "no func in precondition error", - wantErr: true, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - typ: "user", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Precondition: &precondition{ - Query: NewSearchQuery().AddQuery().AggregateIDFilter("hodor").SearchQuery(), - }, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateType: "user", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "validation without precondition ok", - wantErr: false, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - typ: "user", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateType: "user", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - { - name: "validation with precondition ok", - wantErr: false, - fields: fields{aggregate: &Aggregate{ - ID: "aggID", - typ: "user", - version: "v1.0.0", - editorService: "svc", - editorUser: "hodor", - resourceOwner: "org", - PreviousSequence: 5, - Precondition: &precondition{ - Validation: func(...*Event) error { return nil }, - Query: NewSearchQuery().AddQuery().AggregateIDFilter("hodor").SearchQuery(), - }, - Events: []*Event{ - { - AggregateID: "hodor", - AggregateType: "user", - AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - ResourceOwner: "org", - Type: "born", - }}, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.fields.aggregate.Validate() - if (err != nil) != tt.wantErr { - t.Errorf("Aggregate.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - if tt.wantErr && !errors.IsPreconditionFailed(err) { - t.Errorf("error must extend precondition failed: %v", err) - } - }) - } -} - -func TestAggregate_SetPrecondition(t *testing.T) { - type fields struct { - aggregate *Aggregate - } - type args struct { - query *SearchQuery - validateFunc func(...*Event) error - } - tests := []struct { - name string - fields fields - args args - want *Aggregate - }{ - { - name: "set precondition", - fields: fields{aggregate: &Aggregate{}}, - args: args{ - query: &SearchQuery{}, - validateFunc: func(...*Event) error { return nil }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - got := tt.fields.aggregate.SetPrecondition(tt.args.query, tt.args.validateFunc) - if got.Precondition == nil { - t.Error("precondition must not be nil") - t.FailNow() - } - if got.Precondition.Query == nil { - t.Error("query of precondition must not be nil") - } - if got.Precondition.Validation == nil { - t.Error("precondition func must not be nil") - } - }) - } -} diff --git a/internal/eventstore/v1/models/event.go b/internal/eventstore/v1/models/event.go index 0f7a08a76..82f7d3f8e 100644 --- a/internal/eventstore/v1/models/event.go +++ b/internal/eventstore/v1/models/event.go @@ -6,6 +6,7 @@ import ( "time" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" ) type EventType string @@ -14,23 +15,85 @@ func (et EventType) String() string { return string(et) } +var _ eventstore.Event = (*Event)(nil) + type Event struct { ID string - Sequence uint64 + Seq uint64 + Pos float64 CreationDate time.Time - Type EventType + Typ eventstore.EventType PreviousSequence uint64 Data []byte AggregateID string - AggregateType AggregateType - AggregateVersion Version - EditorService string - EditorUser string + AggregateType eventstore.AggregateType + AggregateVersion eventstore.Version + Service string + User string ResourceOwner string InstanceID string } +// Aggregate implements [eventstore.Event] +func (e *Event) Aggregate() *eventstore.Aggregate { + return &eventstore.Aggregate{ + ID: e.AggregateID, + Type: e.AggregateType, + ResourceOwner: e.ResourceOwner, + InstanceID: e.InstanceID, + // Version: eventstore.Version(e.AggregateVersion), + } +} + +// CreatedAt implements [eventstore.Event] +func (e *Event) CreatedAt() time.Time { + return e.CreationDate +} + +// DataAsBytes implements [eventstore.Event] +func (e *Event) DataAsBytes() []byte { + return e.Data +} + +// Unmarshal implements [eventstore.Event] +func (e *Event) Unmarshal(ptr any) error { + if len(e.Data) == 0 { + return nil + } + return json.Unmarshal(e.Data, ptr) +} + +// EditorService implements [eventstore.Event] +func (e *Event) EditorService() string { + return e.Service +} + +// Creator implements [eventstore.action] +func (e *Event) Creator() string { + return e.User +} + +// Sequence implements [eventstore.Event] +func (e *Event) Sequence() uint64 { + return e.Seq +} + +// Position implements [eventstore.Event] +func (e *Event) Position() float64 { + return e.Pos +} + +// Type implements [eventstore.action] +func (e *Event) Type() eventstore.EventType { + return e.Typ +} + +// Type implements [eventstore.action] +func (e *Event) Revision() uint16 { + return 0 +} + func eventData(i interface{}) ([]byte, error) { switch v := i.(type) { case []byte: @@ -63,7 +126,7 @@ func (e *Event) Validate() error { if e == nil { return errors.ThrowPreconditionFailed(nil, "MODEL-oEAG4", "event is nil") } - if string(e.Type) == "" { + if string(e.Typ) == "" { return errors.ThrowPreconditionFailed(nil, "MODEL-R2sB0", "type not defined") } @@ -74,13 +137,12 @@ func (e *Event) Validate() error { return errors.ThrowPreconditionFailed(nil, "MODEL-EzdyK", "aggregate type not set") } if err := e.AggregateVersion.Validate(); err != nil { - return err + return errors.ThrowPreconditionFailed(err, "MODEL-KO71q", "version invalid") } - - if e.EditorService == "" { + if e.Service == "" { return errors.ThrowPreconditionFailed(nil, "MODEL-4Yqik", "editor service not set") } - if e.EditorUser == "" { + if e.User == "" { return errors.ThrowPreconditionFailed(nil, "MODEL-L3NHO", "editor user not set") } if e.ResourceOwner == "" { diff --git a/internal/eventstore/v1/models/event_test.go b/internal/eventstore/v1/models/event_test.go index bcc26c88f..6cd24a167 100644 --- a/internal/eventstore/v1/models/event_test.go +++ b/internal/eventstore/v1/models/event_test.go @@ -95,10 +95,10 @@ func TestEvent_Validate(t *testing.T) { fields: fields{event: &Event{ AggregateType: "user", AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", + Service: "management", + User: "hodor", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: true, }, @@ -107,10 +107,10 @@ func TestEvent_Validate(t *testing.T) { fields: fields{event: &Event{ AggregateID: "hodor", AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", + Service: "management", + User: "hodor", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: true, }, @@ -119,10 +119,10 @@ func TestEvent_Validate(t *testing.T) { fields: fields{event: &Event{ AggregateID: "hodor", AggregateType: "user", - EditorService: "management", - EditorUser: "hodor", + Service: "management", + User: "hodor", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: true, }, @@ -132,9 +132,9 @@ func TestEvent_Validate(t *testing.T) { AggregateID: "hodor", AggregateType: "user", AggregateVersion: "v1.0.0", - EditorUser: "hodor", + User: "hodor", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: true, }, @@ -144,9 +144,9 @@ func TestEvent_Validate(t *testing.T) { AggregateID: "hodor", AggregateType: "user", AggregateVersion: "v1.0.0", - EditorService: "management", + Service: "management", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: true, }, @@ -156,9 +156,9 @@ func TestEvent_Validate(t *testing.T) { AggregateID: "hodor", AggregateType: "user", AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", - Type: "born", + Service: "management", + User: "hodor", + Typ: "born", }}, wantErr: true, }, @@ -168,8 +168,8 @@ func TestEvent_Validate(t *testing.T) { AggregateID: "hodor", AggregateType: "user", AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", + Service: "management", + User: "hodor", ResourceOwner: "org", }}, wantErr: true, @@ -180,10 +180,10 @@ func TestEvent_Validate(t *testing.T) { AggregateID: "hodor", AggregateType: "user", AggregateVersion: "v1.0.0", - EditorService: "management", - EditorUser: "hodor", + Service: "management", + User: "hodor", ResourceOwner: "org", - Type: "born", + Typ: "born", }}, wantErr: false, }, diff --git a/internal/eventstore/v1/models/field.go b/internal/eventstore/v1/models/field.go deleted file mode 100644 index 39435ed5f..000000000 --- a/internal/eventstore/v1/models/field.go +++ /dev/null @@ -1,15 +0,0 @@ -package models - -type Field int32 - -const ( - Field_AggregateType Field = 1 + iota - Field_AggregateID - Field_LatestSequence - Field_ResourceOwner - Field_EditorService - Field_EditorUser - Field_EventType - Field_CreationDate - Field_InstanceID -) diff --git a/internal/eventstore/v1/models/filter.go b/internal/eventstore/v1/models/filter.go deleted file mode 100644 index 5da8ed040..000000000 --- a/internal/eventstore/v1/models/filter.go +++ /dev/null @@ -1,46 +0,0 @@ -package models - -import ( - "github.com/zitadel/zitadel/internal/errors" -) - -type Filter struct { - field Field - value interface{} - operation Operation -} - -//NewFilter is used in tests. Use searchQuery.*Filter() instead -func NewFilter(field Field, value interface{}, operation Operation) *Filter { - return &Filter{ - field: field, - value: value, - operation: operation, - } -} - -func (f *Filter) GetField() Field { - return f.field -} -func (f *Filter) GetOperation() Operation { - return f.operation -} -func (f *Filter) GetValue() interface{} { - return f.value -} - -func (f *Filter) Validate() error { - if f == nil { - return errors.ThrowPreconditionFailed(nil, "MODEL-z6KcG", "filter is nil") - } - if f.field <= 0 { - return errors.ThrowPreconditionFailed(nil, "MODEL-zw62U", "field not definded") - } - if f.value == nil { - return errors.ThrowPreconditionFailed(nil, "MODEL-GJ9ct", "no value definded") - } - if f.operation <= 0 { - return errors.ThrowPreconditionFailed(nil, "MODEL-RrQTy", "operation not definded") - } - return nil -} diff --git a/internal/eventstore/v1/models/filter_test.go b/internal/eventstore/v1/models/filter_test.go deleted file mode 100644 index 5595121c2..000000000 --- a/internal/eventstore/v1/models/filter_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package models - -import ( - "reflect" - "testing" -) - -func TestNewFilter(t *testing.T) { - type args struct { - field Field - value interface{} - operation Operation - } - tests := []struct { - name string - args args - want *Filter - }{ - { - name: "aggregateID equals", - args: args{ - field: Field_AggregateID, - value: "hodor", - operation: Operation_Equals, - }, - want: &Filter{field: Field_AggregateID, operation: Operation_Equals, value: "hodor"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := NewFilter(tt.args.field, tt.args.value, tt.args.operation); !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewFilter() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestFilter_Validate(t *testing.T) { - type fields struct { - field Field - value interface{} - operation Operation - isNil bool - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - { - name: "correct filter", - fields: fields{ - field: Field_LatestSequence, - operation: Operation_Greater, - value: uint64(235), - }, - wantErr: false, - }, - { - name: "filter is nil", - fields: fields{isNil: true}, - wantErr: true, - }, - { - name: "no field error", - fields: fields{ - operation: Operation_Greater, - value: uint64(235), - }, - wantErr: true, - }, - { - name: "no value error", - fields: fields{ - field: Field_LatestSequence, - operation: Operation_Greater, - }, - wantErr: true, - }, - { - name: "no operation error", - fields: fields{ - field: Field_LatestSequence, - value: uint64(235), - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var f *Filter - if !tt.fields.isNil { - f = &Filter{ - field: tt.fields.field, - value: tt.fields.value, - operation: tt.fields.operation, - } - } - if err := f.Validate(); (err != nil) != tt.wantErr { - t.Errorf("Filter.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/internal/eventstore/v1/models/object.go b/internal/eventstore/v1/models/object.go index 3fcb05047..8cde68cfa 100644 --- a/internal/eventstore/v1/models/object.go +++ b/internal/eventstore/v1/models/object.go @@ -2,6 +2,8 @@ package models import ( "time" + + "github.com/zitadel/zitadel/internal/eventstore" ) type ObjectRoot struct { @@ -13,25 +15,25 @@ type ObjectRoot struct { ChangeDate time.Time `json:"-"` } -func (o *ObjectRoot) AppendEvent(event *Event) { +func (o *ObjectRoot) AppendEvent(event eventstore.Event) { if o.AggregateID == "" { - o.AggregateID = event.AggregateID - } else if o.AggregateID != event.AggregateID { + o.AggregateID = event.Aggregate().ID + } else if o.AggregateID != event.Aggregate().ID { return } if o.ResourceOwner == "" { - o.ResourceOwner = event.ResourceOwner + o.ResourceOwner = event.Aggregate().ResourceOwner } if o.InstanceID == "" { - o.InstanceID = event.InstanceID + o.InstanceID = event.Aggregate().InstanceID } - o.ChangeDate = event.CreationDate + o.ChangeDate = event.CreatedAt() if o.CreationDate.IsZero() { o.CreationDate = o.ChangeDate } - o.Sequence = event.Sequence + o.Sequence = event.Sequence() } func (o *ObjectRoot) IsZero() bool { return o.AggregateID == "" diff --git a/internal/eventstore/v1/models/object_test.go b/internal/eventstore/v1/models/object_test.go index 9db230ce7..153103c78 100644 --- a/internal/eventstore/v1/models/object_test.go +++ b/internal/eventstore/v1/models/object_test.go @@ -27,7 +27,7 @@ func TestObjectRoot_AppendEvent(t *testing.T) { args{ &Event{ AggregateID: "aggID", - Sequence: 34555, + Seq: 34555, CreationDate: time.Now(), }, true, @@ -44,7 +44,7 @@ func TestObjectRoot_AppendEvent(t *testing.T) { args{ &Event{ AggregateID: "agg", - Sequence: 34555425, + Seq: 34555425, CreationDate: time.Now(), PreviousSequence: 22, }, @@ -70,8 +70,8 @@ func TestObjectRoot_AppendEvent(t *testing.T) { t.Error("creationDate and changedate should differ") } } - if o.Sequence != tt.args.event.Sequence { - t.Errorf("sequence not equal to event: event: %d root: %d", tt.args.event.Sequence, o.Sequence) + if o.Sequence != tt.args.event.Seq { + t.Errorf("sequence not equal to event: event: %d root: %d", tt.args.event.Seq, o.Sequence) } if !o.ChangeDate.Equal(tt.args.event.CreationDate) { t.Errorf("changedate should be equal to event creation date: event: %v root: %v", tt.args.event.CreationDate, o.ChangeDate) diff --git a/internal/eventstore/v1/models/operation.go b/internal/eventstore/v1/models/operation.go deleted file mode 100644 index dc3bb0696..000000000 --- a/internal/eventstore/v1/models/operation.go +++ /dev/null @@ -1,11 +0,0 @@ -package models - -type Operation int32 - -const ( - Operation_Equals Operation = 1 + iota - Operation_Greater - Operation_Less - Operation_In - Operation_NotIn -) diff --git a/internal/eventstore/v1/models/search_query.go b/internal/eventstore/v1/models/search_query.go deleted file mode 100644 index ea8570ab0..000000000 --- a/internal/eventstore/v1/models/search_query.go +++ /dev/null @@ -1,296 +0,0 @@ -package models - -import ( - "time" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/errors" -) - -type SearchQueryFactory struct { - columns Columns - limit uint64 - desc bool - queries []*query - - InstanceFiltered bool -} - -type query struct { - desc bool - aggregateTypes []AggregateType - aggregateIDs []string - sequenceFrom uint64 - sequenceTo uint64 - eventTypes []EventType - resourceOwner string - instanceID string - ignoredInstanceIDs []string - creationDate time.Time - factory *SearchQueryFactory -} - -type searchQuery struct { - Columns Columns - Limit uint64 - Desc bool - Filters [][]*Filter -} - -type Columns int32 - -const ( - Columns_Event = iota - Columns_Max_Sequence - Columns_InstanceIDs - // insert new columns-types before this columnsCount because count is needed for validation - columnsCount -) - -// FactoryFromSearchQuery is deprecated because it's for migration purposes. use NewSearchQueryFactory -func FactoryFromSearchQuery(q *SearchQuery) *SearchQueryFactory { - factory := &SearchQueryFactory{ - columns: q.Columns, - desc: q.Desc, - limit: q.Limit, - queries: make([]*query, len(q.Queries)), - } - - for i, qq := range q.Queries { - factory.queries[i] = &query{factory: factory} - for _, filter := range qq.Filters { - switch filter.field { - case Field_AggregateType: - factory.queries[i] = factory.queries[i].aggregateTypesMig(filter.value.([]AggregateType)...) - case Field_AggregateID: - if aggregateID, ok := filter.value.(string); ok { - factory.queries[i] = factory.queries[i].AggregateIDs(aggregateID) - } else if aggregateIDs, ok := filter.value.([]string); ok { - factory.queries[i] = factory.queries[i].AggregateIDs(aggregateIDs...) - } - case Field_LatestSequence: - if filter.operation == Operation_Greater { - factory.queries[i] = factory.queries[i].SequenceGreater(filter.value.(uint64)) - } else { - factory.queries[i] = factory.queries[i].SequenceLess(filter.value.(uint64)) - } - case Field_ResourceOwner: - factory.queries[i] = factory.queries[i].ResourceOwner(filter.value.(string)) - case Field_InstanceID: - factory.InstanceFiltered = true - if filter.operation == Operation_Equals { - factory.queries[i] = factory.queries[i].InstanceID(filter.value.(string)) - } else if filter.operation == Operation_NotIn { - factory.queries[i] = factory.queries[i].IgnoredInstanceIDs(filter.value.([]string)...) - } - case Field_EventType: - factory.queries[i] = factory.queries[i].EventTypes(filter.value.([]EventType)...) - case Field_EditorService, Field_EditorUser: - logging.WithFields("value", filter.value).Panic("field not converted to factory") - case Field_CreationDate: - factory.queries[i] = factory.queries[i].CreationDateNewer(filter.value.(time.Time)) - } - } - } - - return factory -} - -func NewSearchQueryFactory() *SearchQueryFactory { - return &SearchQueryFactory{} -} - -func (factory *SearchQueryFactory) Columns(columns Columns) *SearchQueryFactory { - factory.columns = columns - return factory -} - -func (factory *SearchQueryFactory) Limit(limit uint64) *SearchQueryFactory { - factory.limit = limit - return factory -} - -func (factory *SearchQueryFactory) OrderDesc() *SearchQueryFactory { - factory.desc = true - return factory -} - -func (factory *SearchQueryFactory) OrderAsc() *SearchQueryFactory { - factory.desc = false - return factory -} - -func (factory *SearchQueryFactory) AddQuery() *query { - q := &query{factory: factory} - factory.queries = append(factory.queries, q) - return q -} - -func (q *query) Factory() *SearchQueryFactory { - return q.factory -} - -func (q *query) SequenceGreater(sequence uint64) *query { - q.sequenceFrom = sequence - return q -} - -func (q *query) SequenceLess(sequence uint64) *query { - q.sequenceTo = sequence - return q -} - -func (q *query) AggregateTypes(types ...AggregateType) *query { - q.aggregateTypes = types - return q -} - -func (q *query) AggregateIDs(ids ...string) *query { - q.aggregateIDs = ids - return q -} - -func (q *query) aggregateTypesMig(types ...AggregateType) *query { - q.aggregateTypes = types - return q -} - -func (q *query) EventTypes(types ...EventType) *query { - q.eventTypes = types - return q -} - -func (q *query) ResourceOwner(resourceOwner string) *query { - q.resourceOwner = resourceOwner - return q -} - -func (q *query) InstanceID(instanceID string) *query { - q.instanceID = instanceID - return q -} - -func (q *query) IgnoredInstanceIDs(instanceIDs ...string) *query { - q.ignoredInstanceIDs = instanceIDs - return q -} - -func (q *query) CreationDateNewer(time time.Time) *query { - q.creationDate = time - return q -} - -func (factory *SearchQueryFactory) Build() (*searchQuery, error) { - if factory == nil || - len(factory.queries) < 1 || - (factory.columns < 0 || factory.columns >= columnsCount) { - return nil, errors.ThrowPreconditionFailed(nil, "MODEL-tGAD3", "factory invalid") - } - filters := make([][]*Filter, len(factory.queries)) - - for i, query := range factory.queries { - for _, f := range []func() *Filter{ - query.aggregateTypeFilter, - query.aggregateIDFilter, - query.sequenceFromFilter, - query.sequenceToFilter, - query.eventTypeFilter, - query.resourceOwnerFilter, - query.instanceIDFilter, - query.ignoredInstanceIDsFilter, - query.creationDateNewerFilter, - } { - if filter := f(); filter != nil { - filters[i] = append(filters[i], filter) - } - } - } - - return &searchQuery{ - Columns: factory.columns, - Limit: factory.limit, - Desc: factory.desc, - Filters: filters, - }, nil -} - -func (q *query) aggregateIDFilter() *Filter { - if len(q.aggregateIDs) < 1 { - return nil - } - if len(q.aggregateIDs) == 1 { - return NewFilter(Field_AggregateID, q.aggregateIDs[0], Operation_Equals) - } - return NewFilter(Field_AggregateID, q.aggregateIDs, Operation_In) -} - -func (q *query) eventTypeFilter() *Filter { - if len(q.eventTypes) < 1 { - return nil - } - if len(q.eventTypes) == 1 { - return NewFilter(Field_EventType, q.eventTypes[0], Operation_Equals) - } - return NewFilter(Field_EventType, q.eventTypes, Operation_In) -} - -func (q *query) aggregateTypeFilter() *Filter { - if len(q.aggregateTypes) < 1 { - return nil - } - if len(q.aggregateTypes) == 1 { - return NewFilter(Field_AggregateType, q.aggregateTypes[0], Operation_Equals) - } - return NewFilter(Field_AggregateType, q.aggregateTypes, Operation_In) -} - -func (q *query) sequenceFromFilter() *Filter { - if q.sequenceFrom == 0 { - return nil - } - sortOrder := Operation_Greater - if q.factory.desc { - sortOrder = Operation_Less - } - return NewFilter(Field_LatestSequence, q.sequenceFrom, sortOrder) -} - -func (q *query) sequenceToFilter() *Filter { - if q.sequenceTo == 0 { - return nil - } - sortOrder := Operation_Less - if q.factory.desc { - sortOrder = Operation_Greater - } - return NewFilter(Field_LatestSequence, q.sequenceTo, sortOrder) -} - -func (q *query) resourceOwnerFilter() *Filter { - if q.resourceOwner == "" { - return nil - } - return NewFilter(Field_ResourceOwner, q.resourceOwner, Operation_Equals) -} - -func (q *query) instanceIDFilter() *Filter { - if q.instanceID == "" { - return nil - } - return NewFilter(Field_InstanceID, q.instanceID, Operation_Equals) -} - -func (q *query) ignoredInstanceIDsFilter() *Filter { - if len(q.ignoredInstanceIDs) == 0 { - return nil - } - return NewFilter(Field_InstanceID, q.ignoredInstanceIDs, Operation_NotIn) -} - -func (q *query) creationDateNewerFilter() *Filter { - if q.creationDate.IsZero() { - return nil - } - return NewFilter(Field_CreationDate, q.creationDate, Operation_Greater) -} diff --git a/internal/eventstore/v1/models/search_query_old.go b/internal/eventstore/v1/models/search_query_old.go deleted file mode 100644 index eed8da3ce..000000000 --- a/internal/eventstore/v1/models/search_query_old.go +++ /dev/null @@ -1,148 +0,0 @@ -package models - -import ( - "time" - - "github.com/zitadel/zitadel/internal/errors" -) - -//SearchQuery is deprecated. Use SearchQueryFactory -type SearchQuery struct { - Columns Columns - Limit uint64 - Desc bool - Filters []*Filter - Queries []*Query -} - -type Query struct { - searchQuery *SearchQuery - Filters []*Filter -} - -//NewSearchQuery is deprecated. Use SearchQueryFactory -func NewSearchQuery() *SearchQuery { - return &SearchQuery{ - Filters: make([]*Filter, 0, 4), - Queries: make([]*Query, 0), - } -} - -func (q *SearchQuery) SetColumn(columns Columns) *SearchQuery { - q.Columns = columns - return q -} - -func (q *SearchQuery) AddQuery() *Query { - query := &Query{ - searchQuery: q, - } - q.Queries = append(q.Queries, query) - - return query -} - -//SearchQuery returns the SearchQuery of the sub query -func (q *Query) SearchQuery() *SearchQuery { - return q.searchQuery -} -func (q *Query) setFilter(filter *Filter) *Query { - for i, f := range q.Filters { - if f.field == filter.field && f.field != Field_LatestSequence { - q.Filters[i] = filter - return q - } - } - q.Filters = append(q.Filters, filter) - return q -} - -func (q *SearchQuery) SetLimit(limit uint64) *SearchQuery { - q.Limit = limit - return q -} - -func (q *SearchQuery) OrderDesc() *SearchQuery { - q.Desc = true - return q -} - -func (q *SearchQuery) OrderAsc() *SearchQuery { - q.Desc = false - return q -} - -func (q *Query) AggregateIDFilter(id string) *Query { - return q.setFilter(NewFilter(Field_AggregateID, id, Operation_Equals)) -} - -func (q *Query) AggregateIDsFilter(ids ...string) *Query { - return q.setFilter(NewFilter(Field_AggregateID, ids, Operation_In)) -} - -func (q *Query) AggregateTypeFilter(types ...AggregateType) *Query { - return q.setFilter(NewFilter(Field_AggregateType, types, Operation_In)) -} - -func (q *Query) EventTypesFilter(types ...EventType) *Query { - return q.setFilter(NewFilter(Field_EventType, types, Operation_In)) -} - -func (q *Query) LatestSequenceFilter(sequence uint64) *Query { - if sequence == 0 { - return q - } - sortOrder := Operation_Greater - return q.setFilter(NewFilter(Field_LatestSequence, sequence, sortOrder)) -} - -func (q *Query) SequenceBetween(from, to uint64) *Query { - q.setFilter(NewFilter(Field_LatestSequence, from, Operation_Greater)) - q.setFilter(NewFilter(Field_LatestSequence, to, Operation_Less)) - return q -} - -func (q *Query) ResourceOwnerFilter(resourceOwner string) *Query { - return q.setFilter(NewFilter(Field_ResourceOwner, resourceOwner, Operation_Equals)) -} - -func (q *Query) InstanceIDFilter(instanceID string) *Query { - return q.setFilter(NewFilter(Field_InstanceID, instanceID, Operation_Equals)) -} - -func (q *Query) ExcludedInstanceIDsFilter(instanceIDs ...string) *Query { - return q.setFilter(NewFilter(Field_InstanceID, instanceIDs, Operation_NotIn)) -} - -func (q *Query) CreationDateNewerFilter(time time.Time) *Query { - return q.setFilter(NewFilter(Field_CreationDate, time, Operation_Greater)) -} - -func (q *SearchQuery) setFilter(filter *Filter) *SearchQuery { - for i, f := range q.Filters { - if f.field == filter.field && f.field != Field_LatestSequence { - q.Filters[i] = filter - return q - } - } - q.Filters = append(q.Filters, filter) - return q -} - -func (q *SearchQuery) Validate() error { - if q == nil { - return errors.ThrowPreconditionFailed(nil, "MODEL-J5xQi", "search query is nil") - } - if len(q.Queries) == 0 { - return errors.ThrowPreconditionFailed(nil, "MODEL-pF3DR", "no filters set") - } - for _, query := range q.Queries { - for _, filter := range query.Filters { - if err := filter.Validate(); err != nil { - return err - } - } - } - - return nil -} diff --git a/internal/eventstore/v1/models/search_query_old_test.go b/internal/eventstore/v1/models/search_query_old_test.go deleted file mode 100644 index 1736f6131..000000000 --- a/internal/eventstore/v1/models/search_query_old_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package models - -import ( - "reflect" - "testing" -) - -func TestSearchQuery_setFilter(t *testing.T) { - type fields struct { - query *SearchQuery - } - type args struct { - filters []*Filter - } - tests := []struct { - name string - fields fields - args args - want *SearchQuery - }{ - { - name: "set idFilter", - fields: fields{query: NewSearchQuery()}, - args: args{filters: []*Filter{ - {field: Field_AggregateID, operation: Operation_Equals, value: "hodor"}, - }}, - want: &SearchQuery{Filters: []*Filter{ - {field: Field_AggregateID, operation: Operation_Equals, value: "hodor"}, - }}, - }, - { - name: "overwrite idFilter", - fields: fields{query: NewSearchQuery()}, - args: args{filters: []*Filter{ - {field: Field_AggregateID, operation: Operation_Equals, value: "hodor"}, - {field: Field_AggregateID, operation: Operation_Equals, value: "ursli"}, - }}, - want: &SearchQuery{Filters: []*Filter{ - {field: Field_AggregateID, operation: Operation_Equals, value: "ursli"}, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := tt.fields.query - for _, filter := range tt.args.filters { - got = got.setFilter(filter) - } - for _, wantFilter := range tt.want.Filters { - found := false - for _, gotFilter := range got.Filters { - if gotFilter.field == wantFilter.field { - found = true - if !reflect.DeepEqual(wantFilter, gotFilter) { - t.Errorf("filter not as expected: want: %v got %v", wantFilter, gotFilter) - } - } - } - if !found { - t.Errorf("filter field %v not found", wantFilter.field) - } - } - }) - } -} diff --git a/internal/eventstore/v1/models/search_query_test.go b/internal/eventstore/v1/models/search_query_test.go deleted file mode 100644 index 9abd19937..000000000 --- a/internal/eventstore/v1/models/search_query_test.go +++ /dev/null @@ -1,590 +0,0 @@ -package models - -import ( - "reflect" - "testing" - - "github.com/zitadel/zitadel/internal/errors" -) - -func testSetColumns(columns Columns) func(factory *SearchQueryFactory) *SearchQueryFactory { - return func(factory *SearchQueryFactory) *SearchQueryFactory { - factory = factory.Columns(columns) - return factory - } -} - -func testSetLimit(limit uint64) func(factory *SearchQueryFactory) *SearchQueryFactory { - return func(factory *SearchQueryFactory) *SearchQueryFactory { - factory = factory.Limit(limit) - return factory - } -} - -func testAddQuery(queryFuncs ...func(*query) *query) func(*SearchQueryFactory) *SearchQueryFactory { - return func(builder *SearchQueryFactory) *SearchQueryFactory { - query := builder.AddQuery() - for _, queryFunc := range queryFuncs { - queryFunc(query) - } - return query.Factory() - } -} - -func testSetSequence(sequence uint64) func(*query) *query { - return func(q *query) *query { - q.SequenceGreater(sequence) - return q - } -} - -func testSetAggregateIDs(aggregateIDs ...string) func(*query) *query { - return func(q *query) *query { - q.AggregateIDs(aggregateIDs...) - return q - } -} - -func testSetAggregateTypes(aggregateTypes ...AggregateType) func(*query) *query { - return func(q *query) *query { - q.AggregateTypes(aggregateTypes...) - return q - } -} - -func testSetEventTypes(eventTypes ...EventType) func(*query) *query { - return func(q *query) *query { - q.EventTypes(eventTypes...) - return q - } -} - -func testSetResourceOwner(resourceOwner string) func(*query) *query { - return func(q *query) *query { - q.ResourceOwner(resourceOwner) - return q - } -} - -func testSetSortOrder(asc bool) func(factory *SearchQueryFactory) *SearchQueryFactory { - return func(factory *SearchQueryFactory) *SearchQueryFactory { - if asc { - factory = factory.OrderAsc() - } else { - factory = factory.OrderDesc() - } - return factory - } -} - -func assertFactory(t *testing.T, want, got *SearchQueryFactory) { - t.Helper() - - if got.columns != want.columns { - t.Errorf("wrong column: got: %v want: %v", got.columns, want.columns) - } - if got.desc != want.desc { - t.Errorf("wrong desc: got: %v want: %v", got.desc, want.desc) - } - if got.limit != want.limit { - t.Errorf("wrong limit: got: %v want: %v", got.limit, want.limit) - } - if len(got.queries) != len(want.queries) { - t.Errorf("wrong length of queries: got: %v want: %v", len(got.queries), len(want.queries)) - } - - for i, query := range got.queries { - assertQuery(t, i, want.queries[i], query) - } -} - -func assertQuery(t *testing.T, i int, want, got *query) { - t.Helper() - - if !reflect.DeepEqual(got.aggregateIDs, want.aggregateIDs) { - t.Errorf("wrong aggregateIDs in query %d : got: %v want: %v", i, got.aggregateIDs, want.aggregateIDs) - } - if !reflect.DeepEqual(got.aggregateTypes, want.aggregateTypes) { - t.Errorf("wrong aggregateTypes in query %d : got: %v want: %v", i, got.aggregateTypes, want.aggregateTypes) - } - if got.sequenceFrom != want.sequenceFrom { - t.Errorf("wrong sequenceFrom in query %d : got: %v want: %v", i, got.sequenceFrom, want.sequenceFrom) - } - if got.sequenceTo != want.sequenceTo { - t.Errorf("wrong sequenceTo in query %d : got: %v want: %v", i, got.sequenceTo, want.sequenceTo) - } - if !reflect.DeepEqual(got.eventTypes, want.eventTypes) { - t.Errorf("wrong eventTypes in query %d : got: %v want: %v", i, got.eventTypes, want.eventTypes) - } -} - -func TestSearchQueryFactorySetters(t *testing.T) { - type args struct { - setters []func(*SearchQueryFactory) *SearchQueryFactory - } - tests := []struct { - name string - args args - res *SearchQueryFactory - }{ - { - name: "New factory", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{}, - }, - res: &SearchQueryFactory{}, - }, - { - name: "set columns", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testSetColumns(Columns_Max_Sequence)}, - }, - res: &SearchQueryFactory{ - columns: Columns_Max_Sequence, - }, - }, - { - name: "set limit", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testSetLimit(100)}, - }, - res: &SearchQueryFactory{ - limit: 100, - }, - }, - { - name: "set sequence", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetSequence(90))}, - }, - res: &SearchQueryFactory{ - queries: []*query{ - { - sequenceFrom: 90, - }, - }, - }, - }, - { - name: "set aggregateTypes", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetAggregateTypes("user", "org"))}, - }, - res: &SearchQueryFactory{ - queries: []*query{ - { - aggregateTypes: []AggregateType{"user", "org"}, - }, - }, - }, - }, - { - name: "set aggregateIDs", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetAggregateIDs("1235", "09824"))}, - }, - res: &SearchQueryFactory{ - queries: []*query{ - { - aggregateIDs: []string{"1235", "09824"}, - }, - }, - }, - }, - { - name: "set eventTypes", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetEventTypes("user.created", "user.updated"))}, - }, - res: &SearchQueryFactory{ - queries: []*query{ - { - eventTypes: []EventType{"user.created", "user.updated"}, - }, - }, - }, - }, - { - name: "set resource owner", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetResourceOwner("hodor"))}, - }, - res: &SearchQueryFactory{ - queries: []*query{ - { - resourceOwner: "hodor", - }, - }, - }, - }, - { - name: "default search query", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{testAddQuery(testSetAggregateTypes("user"), testSetAggregateIDs("1235", "024")), testSetSortOrder(false)}, - }, - res: &SearchQueryFactory{ - desc: true, - queries: []*query{ - { - aggregateTypes: []AggregateType{"user"}, - aggregateIDs: []string{"1235", "024"}, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - factory := NewSearchQueryFactory() - for _, setter := range tt.args.setters { - factory = setter(factory) - } - assertFactory(t, tt.res, factory) - }) - } -} - -func TestSearchQueryFactoryBuild(t *testing.T) { - type args struct { - setters []func(*SearchQueryFactory) *SearchQueryFactory - } - type res struct { - isErr func(err error) bool - query *searchQuery - } - tests := []struct { - name string - args args - res res - }{ - { - name: "no aggregate types", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{}, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - query: nil, - }, - }, - { - name: "invalid column (too low)", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testSetColumns(Columns(-1)), - testAddQuery(testSetAggregateTypes("user")), - }, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - }, - }, - { - name: "invalid column (too high)", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testSetColumns(columnsCount), - testAddQuery(testSetAggregateTypes("user")), - }, - }, - res: res{ - isErr: errors.IsPreconditionFailed, - }, - }, - { - name: "filter aggregate type", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery(testSetAggregateTypes("user")), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - }, - }, - }, - }, - }, - { - name: "filter aggregate types", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery(testSetAggregateTypes("user", "org")), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, []AggregateType{"user", "org"}, Operation_In), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, desc", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testSetLimit(5), - testSetSortOrder(false), - testAddQuery( - testSetAggregateTypes("user"), - testSetSequence(100), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: true, - Limit: 5, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_LatestSequence, uint64(100), Operation_Less), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, asc", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testSetLimit(5), - testSetSortOrder(true), - testAddQuery( - testSetSequence(100), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 5, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_LatestSequence, uint64(100), Operation_Greater), - }, - }, - }, - }, - }, - { - name: "filter aggregate type, limit, desc, max event sequence cols", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testSetLimit(5), - testSetSortOrder(false), - testSetColumns(Columns_Max_Sequence), - testAddQuery( - testSetSequence(100), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: Columns_Max_Sequence, - Desc: true, - Limit: 5, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_LatestSequence, uint64(100), Operation_Less), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and aggregate id", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetAggregateIDs("1234"), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_AggregateID, "1234", Operation_Equals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and aggregate ids", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetAggregateIDs("1234", "0815"), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_AggregateID, []string{"1234", "0815"}, Operation_In), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and sequence greater", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetSequence(8), - testSetAggregateTypes("user"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_LatestSequence, uint64(8), Operation_Greater), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and event type", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetAggregateTypes("user"), - testSetEventTypes("user.created"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_EventType, EventType("user.created"), Operation_Equals), - }, - }, - }, - }, - }, - { - name: "filter aggregate type and event types", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetAggregateTypes("user"), - testSetEventTypes("user.created", "user.changed"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_EventType, []EventType{"user.created", "user.changed"}, Operation_In), - }, - }, - }, - }, - }, - { - name: "filter aggregate type resource owner", - args: args{ - setters: []func(*SearchQueryFactory) *SearchQueryFactory{ - testAddQuery( - testSetAggregateTypes("user"), - testSetResourceOwner("hodor"), - ), - }, - }, - res: res{ - isErr: nil, - query: &searchQuery{ - Columns: 0, - Desc: false, - Limit: 0, - Filters: [][]*Filter{ - { - NewFilter(Field_AggregateType, AggregateType("user"), Operation_Equals), - NewFilter(Field_ResourceOwner, "hodor", Operation_Equals), - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - factory := NewSearchQueryFactory() - for _, f := range tt.args.setters { - factory = f(factory) - } - query, err := factory.Build() - if tt.res.isErr != nil && !tt.res.isErr(err) { - t.Errorf("wrong error: %v", err) - return - } - if err != nil && tt.res.isErr == nil { - t.Errorf("no error expected: %v", err) - return - } - - if !reflect.DeepEqual(query, tt.res.query) { - t.Errorf("NewSearchQueryFactory() = %v, want %v", factory, tt.res) - } - }) - } -} diff --git a/internal/eventstore/v1/models/version.go b/internal/eventstore/v1/models/version.go deleted file mode 100644 index 3fba9a534..000000000 --- a/internal/eventstore/v1/models/version.go +++ /dev/null @@ -1,22 +0,0 @@ -package models - -import ( - "regexp" - - "github.com/zitadel/zitadel/internal/errors" -) - -var versionRegexp = regexp.MustCompile(`^v[0-9]+(\.[0-9]+){0,2}$`) - -type Version string - -func (v Version) Validate() error { - if !versionRegexp.MatchString(string(v)) { - return errors.ThrowPreconditionFailed(nil, "MODEL-luDuS", "version is not semver") - } - return nil -} - -func (v Version) String() string { - return string(v) -} diff --git a/internal/eventstore/v1/models/version_test.go b/internal/eventstore/v1/models/version_test.go deleted file mode 100644 index fe8a2fe4b..000000000 --- a/internal/eventstore/v1/models/version_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package models - -import "testing" - -func TestVersion_Validate(t *testing.T) { - tests := []struct { - name string - v Version - wantErr bool - }{ - { - "correct version", - "v1.23.23", - false, - }, - { - "no v prefix", - "1.2.2", - true, - }, - { - "letters in version", - "v1.as.3", - true, - }, - { - "no version", - "", - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.v.Validate(); (err != nil) != tt.wantErr { - t.Errorf("Version.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/internal/eventstore/v1/query/handler.go b/internal/eventstore/v1/query/handler.go deleted file mode 100755 index 384d5206f..000000000 --- a/internal/eventstore/v1/query/handler.go +++ /dev/null @@ -1,93 +0,0 @@ -package query - -import ( - "context" - "runtime/debug" - "time" - - "github.com/zitadel/logging" - - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -const ( - eventLimit = 10000 -) - -type Handler interface { - ViewModel() string - EventQuery(ctx context.Context, instanceIDs []string) (*models.SearchQuery, error) - Reduce(*models.Event) error - OnError(event *models.Event, err error) error - OnSuccess(instanceIDs []string) error - MinimumCycleDuration() time.Duration - LockDuration() time.Duration - QueryLimit() uint64 - - AggregateTypes() []models.AggregateType - CurrentSequence(ctx context.Context, instanceID string) (uint64, error) - Eventstore() v1.Eventstore - - Subscription() *v1.Subscription -} - -func ReduceEvent(ctx context.Context, handler Handler, event *models.Event) { - defer func() { - err := recover() - - if err != nil { - handler.Subscription().Unsubscribe() - logging.WithFields( - "cause", err, - "stack", string(debug.Stack()), - "sequence", event.Sequence, - "instance", event.InstanceID, - ).Error("reduce panicked") - } - }() - currentSequence, err := handler.CurrentSequence(ctx, event.InstanceID) - if err != nil { - logging.WithError(err).Warn("unable to get current sequence") - return - } - - searchQuery := models.NewSearchQuery(). - AddQuery(). - AggregateTypeFilter(handler.AggregateTypes()...). - SequenceBetween(currentSequence, event.Sequence). - InstanceIDFilter(event.InstanceID). - SearchQuery(). - SetLimit(eventLimit) - - unprocessedEvents, err := handler.Eventstore().FilterEvents(ctx, searchQuery) - if err != nil { - logging.WithFields("sequence", event.Sequence).Warn("filter failed") - return - } - - for _, unprocessedEvent := range unprocessedEvents { - currentSequence, err := handler.CurrentSequence(ctx, unprocessedEvent.InstanceID) - if err != nil { - logging.WithError(err).Warn("unable to get current sequence") - return - } - if unprocessedEvent.Sequence < currentSequence { - logging.WithFields( - "unprocessed", unprocessedEvent.Sequence, - "current", currentSequence, - "view", handler.ViewModel()). - Warn("sequence not matching") - return - } - - err = handler.Reduce(unprocessedEvent) - logging.WithFields("sequence", unprocessedEvent.Sequence).OnError(err).Warn("reduce failed") - } - if len(unprocessedEvents) == eventLimit { - logging.WithFields("sequence", event.Sequence).Warn("didnt process event") - return - } - err = handler.Reduce(event) - logging.WithFields("sequence", event.Sequence).OnError(err).Warn("reduce failed") -} diff --git a/internal/eventstore/v1/sdk/append_event_error.go b/internal/eventstore/v1/sdk/append_event_error.go deleted file mode 100644 index f70b34e70..000000000 --- a/internal/eventstore/v1/sdk/append_event_error.go +++ /dev/null @@ -1,36 +0,0 @@ -package sdk - -import ( - "fmt" - - "github.com/zitadel/zitadel/internal/errors" -) - -var ( - _ AppendEventError = (*appendEventError)(nil) - _ errors.Error = (*appendEventError)(nil) -) - -type AppendEventError interface { - error - IsAppendEventError() -} - -type appendEventError struct { - *errors.CaosError -} - -func ThrowAppendEventError(parent error, id, message string) error { - return &appendEventError{errors.CreateCaosError(parent, id, message)} -} - -func ThrowAggregaterf(parent error, id, format string, a ...interface{}) error { - return ThrowAppendEventError(parent, id, fmt.Sprintf(format, a...)) -} - -func (err *appendEventError) IsAppendEventError() {} - -func IsAppendEventError(err error) bool { - _, ok := err.(AppendEventError) - return ok -} diff --git a/internal/eventstore/v1/sdk/append_event_error_test.go b/internal/eventstore/v1/sdk/append_event_error_test.go deleted file mode 100644 index cb1c67cd2..000000000 --- a/internal/eventstore/v1/sdk/append_event_error_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package sdk - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAppendEventError(t *testing.T) { - var err interface{} - err = new(appendEventError) - _, ok := err.(*appendEventError) - assert.True(t, ok) -} - -func TestThrowAppendEventErrorf(t *testing.T) { - err := ThrowAggregaterf(nil, "id", "msg") - _, ok := err.(*appendEventError) - assert.True(t, ok) -} - -func TestIsAppendEventError(t *testing.T) { - err := ThrowAppendEventError(nil, "id", "msg") - ok := IsAppendEventError(err) - assert.True(t, ok) - - err = errors.New("i am found") - ok = IsAppendEventError(err) - assert.False(t, ok) -} diff --git a/internal/eventstore/v1/sdk/sdk.go b/internal/eventstore/v1/sdk/sdk.go deleted file mode 100644 index 0bca75893..000000000 --- a/internal/eventstore/v1/sdk/sdk.go +++ /dev/null @@ -1,27 +0,0 @@ -package sdk - -import ( - "context" - - "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -type filterFunc func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) -type appendFunc func(...*es_models.Event) error -type AggregateFunc func(context.Context) (*es_models.Aggregate, error) - -func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query *es_models.SearchQuery) error { - events, err := filter(ctx, query) - if err != nil { - return err - } - if len(events) == 0 { - return errors.ThrowNotFound(nil, "EVENT-8due3", "no events found") - } - err = appender(events...) - if err != nil { - return ThrowAppendEventError(err, "SDK-awiWK", "Errors.Internal") - } - return nil -} diff --git a/internal/eventstore/v1/sdk/sdk_test.go b/internal/eventstore/v1/sdk/sdk_test.go deleted file mode 100644 index 01a41e520..000000000 --- a/internal/eventstore/v1/sdk/sdk_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package sdk - -import ( - "context" - "testing" - - "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -func TestFilter(t *testing.T) { - type args struct { - filter filterFunc - appender appendFunc - } - tests := []struct { - name string - args args - wantErr func(error) bool - }{ - { - name: "filter error", - args: args{ - filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) { - return nil, errors.ThrowInternal(nil, "test-46VX2", "test error") - }, - appender: nil, - }, - wantErr: errors.IsInternal, - }, - { - name: "no events found", - args: args{ - filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) { - return []*es_models.Event{}, nil - }, - appender: nil, - }, - wantErr: errors.IsNotFound, - }, - { - name: "append fails", - args: args{ - filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) { - return []*es_models.Event{&es_models.Event{}}, nil - }, - appender: func(...*es_models.Event) error { - return errors.ThrowInvalidArgument(nil, "SDK-DhBzl", "test error") - }, - }, - wantErr: IsAppendEventError, - }, - { - name: "filter correct", - args: args{ - filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) { - return []*es_models.Event{&es_models.Event{}}, nil - }, - appender: func(...*es_models.Event) error { - return nil - }, - }, - wantErr: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := Filter(context.Background(), tt.args.filter, tt.args.appender, nil) - if tt.wantErr == nil && err != nil { - t.Errorf("no error expected %v", err) - } - if tt.wantErr != nil && !tt.wantErr(err) { - t.Errorf("no error has wrong type %v", err) - } - }) - } -} diff --git a/internal/eventstore/v1/spooler/config.go b/internal/eventstore/v1/spooler/config.go deleted file mode 100644 index 92f2de129..000000000 --- a/internal/eventstore/v1/spooler/config.go +++ /dev/null @@ -1,42 +0,0 @@ -package spooler - -import ( - "math/rand" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/id" -) - -type Config struct { - Eventstore v1.Eventstore - EventstoreV2 *eventstore.Eventstore - Locker Locker - ViewHandlers []query.Handler - ConcurrentWorkers int - ConcurrentInstances int -} - -func (c *Config) New() *Spooler { - lockID, err := id.SonyFlakeGenerator().Next() - logging.OnError(err).Panic("unable to generate lockID") - - //shuffle the handlers for better balance when running multiple pods - rand.Shuffle(len(c.ViewHandlers), func(i, j int) { - c.ViewHandlers[i], c.ViewHandlers[j] = c.ViewHandlers[j], c.ViewHandlers[i] - }) - - return &Spooler{ - handlers: c.ViewHandlers, - lockID: lockID, - eventstore: c.Eventstore, - esV2: c.EventstoreV2, - locker: c.Locker, - queue: make(chan *spooledHandler, len(c.ViewHandlers)), - workers: c.ConcurrentWorkers, - concurrentInstances: c.ConcurrentInstances, - } -} diff --git a/internal/eventstore/v1/spooler/mock.go b/internal/eventstore/v1/spooler/mock.go deleted file mode 100644 index 04c449aa9..000000000 --- a/internal/eventstore/v1/spooler/mock.go +++ /dev/null @@ -1,3 +0,0 @@ -package spooler - -//go:generate mockgen -source spooler.go -destination ./mock/spooler.go -package mock diff --git a/internal/eventstore/v1/spooler/mock/spooler.go b/internal/eventstore/v1/spooler/mock/spooler.go deleted file mode 100644 index c0e3cb224..000000000 --- a/internal/eventstore/v1/spooler/mock/spooler.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: spooler.go - -// Package mock is a generated GoMock package. -package mock - -import ( - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" -) - -// MockLocker is a mock of Locker interface. -type MockLocker struct { - ctrl *gomock.Controller - recorder *MockLockerMockRecorder -} - -// MockLockerMockRecorder is the mock recorder for MockLocker. -type MockLockerMockRecorder struct { - mock *MockLocker -} - -// NewMockLocker creates a new mock instance. -func NewMockLocker(ctrl *gomock.Controller) *MockLocker { - mock := &MockLocker{ctrl: ctrl} - mock.recorder = &MockLockerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLocker) EXPECT() *MockLockerMockRecorder { - return m.recorder -} - -// Renew mocks base method. -func (m *MockLocker) Renew(lockerID, viewModel, instanceID string, waitTime time.Duration) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Renew", lockerID, viewModel, instanceID, waitTime) - ret0, _ := ret[0].(error) - return ret0 -} - -// Renew indicates an expected call of Renew. -func (mr *MockLockerMockRecorder) Renew(lockerID, viewModel, instanceID, waitTime interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Renew", reflect.TypeOf((*MockLocker)(nil).Renew), lockerID, viewModel, instanceID, waitTime) -} diff --git a/internal/eventstore/v1/spooler/spooler.go b/internal/eventstore/v1/spooler/spooler.go deleted file mode 100644 index 6291e448d..000000000 --- a/internal/eventstore/v1/spooler/spooler.go +++ /dev/null @@ -1,292 +0,0 @@ -package spooler - -import ( - "context" - "runtime/debug" - "strconv" - "sync" - "time" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/telemetry/tracing" - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - systemID = "system" - schedulerSucceeded = eventstore.EventType("system.projections.scheduler.succeeded") - aggregateType = eventstore.AggregateType("system") - aggregateID = "SYSTEM" -) - -type Spooler struct { - handlers []query.Handler - locker Locker - lockID string - eventstore v1.Eventstore - esV2 *eventstore.Eventstore - workers int - queue chan *spooledHandler - concurrentInstances int -} - -type Locker interface { - Renew(lockerID, viewModel, instanceID string, waitTime time.Duration) error -} - -type spooledHandler struct { - query.Handler - locker Locker - queuedAt time.Time - eventstore v1.Eventstore - esV2 *eventstore.Eventstore - concurrentInstances int - succeededOnce bool -} - -func (s *Spooler) Start() { - defer logging.WithFields("lockerID", s.lockID, "workers", s.workers).Info("spooler started") - if s.workers < 1 { - return - } - - for i := 0; i < s.workers; i++ { - go func(workerIdx int) { - workerID := s.lockID + "--" + strconv.Itoa(workerIdx) - for task := range s.queue { - go requeueTask(task, s.queue) - task.load(workerID) - } - }(i) - } - go func() { - for _, handler := range s.handlers { - s.queue <- &spooledHandler{Handler: handler, locker: s.locker, queuedAt: time.Now(), eventstore: s.eventstore, esV2: s.esV2, concurrentInstances: s.concurrentInstances} - } - }() -} - -func requeueTask(task *spooledHandler, queue chan<- *spooledHandler) { - time.Sleep(task.MinimumCycleDuration() - time.Since(task.queuedAt)) - task.queuedAt = time.Now() - queue <- task -} - -func (s *spooledHandler) hasSucceededOnce(ctx context.Context) (bool, error) { - events, err := s.esV2.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AddQuery(). - AggregateTypes(aggregateType). - AggregateIDs(aggregateID). - EventTypes(schedulerSucceeded). - EventData(map[string]interface{}{ - "name": s.ViewModel(), - }). - Builder(), - ) - return len(events) > 0 && err == nil, err -} - -func (s *spooledHandler) setSucceededOnce(ctx context.Context) error { - _, err := s.esV2.Push(ctx, &handler.ProjectionSucceededEvent{ - BaseEvent: *eventstore.NewBaseEventForPush(ctx, - eventstore.NewAggregate(ctx, aggregateID, aggregateType, "v1"), - schedulerSucceeded, - ), - Name: s.ViewModel(), - }) - s.succeededOnce = err == nil - return err -} - -func (s *spooledHandler) load(workerID string) { - errs := make(chan error) - defer func() { - close(errs) - err := recover() - - if err != nil { - logging.WithFields( - "cause", err, - "stack", string(debug.Stack()), - ).Error("reduce panicked") - } - }() - ctx, cancel := context.WithCancel(context.Background()) - go s.awaitError(cancel, errs, workerID) - hasLocked := s.lock(ctx, errs, workerID) - - if <-hasLocked { - if !s.succeededOnce { - var err error - s.succeededOnce, err = s.hasSucceededOnce(ctx) - if err != nil { - logging.WithFields("view", s.ViewModel()).OnError(err).Debug("initial lock failed for first schedule") - errs <- err - return - } - } - - instanceIDQuery := models.NewSearchQuery().SetColumn(models.Columns_InstanceIDs).AddQuery().ExcludedInstanceIDsFilter("") - for { - if s.succeededOnce { - // since we have at least one successful run, we can restrict it to events not older than - // twice the requeue time (just to be sure not to miss an event) - instanceIDQuery = instanceIDQuery.CreationDateNewerFilter(time.Now().Add(-2 * s.MinimumCycleDuration())) - } - ids, err := s.eventstore.InstanceIDs(ctx, instanceIDQuery.SearchQuery()) - if err != nil { - errs <- err - break - } - for i := 0; i < len(ids); i = i + s.concurrentInstances { - max := i + s.concurrentInstances - if max > len(ids) { - max = len(ids) - } - err = s.processInstances(ctx, workerID, ids[i:max]) - if err != nil { - errs <- err - } - } - if ctx.Err() == nil { - if !s.succeededOnce { - err = s.setSucceededOnce(ctx) - logging.WithFields("view", s.ViewModel()).OnError(err).Warn("unable to push first schedule succeeded") - } - errs <- nil - } - break - } - } - <-ctx.Done() -} - -func (s *spooledHandler) processInstances(ctx context.Context, workerID string, ids []string) error { - for { - processCtx, cancel := context.WithTimeout(ctx, 30*time.Second) - events, err := s.query(processCtx, ids) - if err != nil { - cancel() - return err - } - if len(events) == 0 { - cancel() - return nil - } - err = s.process(processCtx, events, workerID, ids) - cancel() - if err != nil { - return err - } - if uint64(len(events)) < s.QueryLimit() { - // no more events to process - return nil - } - } -} - -func (s *spooledHandler) awaitError(cancel func(), errs chan error, workerID string) { - select { - case err := <-errs: - cancel() - logging.OnError(err).WithField("view", s.ViewModel()).WithField("worker", workerID).Debug("load canceled") - } -} - -func (s *spooledHandler) process(ctx context.Context, events []*models.Event, workerID string, instanceIDs []string) error { - for i, event := range events { - select { - case <-ctx.Done(): - logging.WithFields("view", s.ViewModel(), "worker", workerID, "traceID", tracing.TraceIDFromCtx(ctx)).Debug("context canceled") - return nil - default: - if err := s.Reduce(event); err != nil { - err = s.OnError(event, err) - if err == nil { - continue - } - time.Sleep(100 * time.Millisecond) - return s.process(ctx, events[i:], workerID, instanceIDs) - } - } - } - err := s.OnSuccess(instanceIDs) - logging.WithFields("view", s.ViewModel(), "worker", workerID, "traceID", tracing.TraceIDFromCtx(ctx)).OnError(err).Warn("could not process on success func") - return err -} - -func (s *spooledHandler) query(ctx context.Context, instanceIDs []string) ([]*models.Event, error) { - query, err := s.EventQuery(ctx, instanceIDs) - if err != nil { - return nil, err - } - query.Limit = s.QueryLimit() - return s.eventstore.FilterEvents(ctx, query) -} - -// lock ensures the lock on the database. -// the returned channel will be closed if ctx is done or an error occured durring lock -func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID string) chan bool { - renewTimer := time.After(0) - locked := make(chan bool) - - go func(locked chan bool) { - var firstLock sync.Once - defer close(locked) - for { - select { - case <-ctx.Done(): - return - case <-renewTimer: - err := s.locker.Renew(workerID, s.ViewModel(), systemID, s.LockDuration()) - firstLock.Do(func() { - locked <- err == nil - }) - if err == nil { - renewTimer = time.After(s.LockDuration()) - continue - } - - if ctx.Err() == nil { - errs <- err - } - return - } - } - }(locked) - - return locked -} - -func HandleError(event *models.Event, failedErr error, - latestFailedEvent func(sequence uint64, instanceID string) (*repository.FailedEvent, error), - processFailedEvent func(*repository.FailedEvent) error, - processSequence func(*models.Event) error, - errorCountUntilSkip uint64) error { - failedEvent, err := latestFailedEvent(event.Sequence, event.InstanceID) - if err != nil { - return err - } - failedEvent.FailureCount++ - failedEvent.ErrMsg = failedErr.Error() - failedEvent.InstanceID = event.InstanceID - failedEvent.LastFailed = time.Now() - err = processFailedEvent(failedEvent) - if err != nil { - return err - } - if errorCountUntilSkip <= failedEvent.FailureCount { - return processSequence(event) - } - return failedErr -} - -func HandleSuccess(updateSpoolerRunTimestamp func([]string) error, instanceIDs []string) error { - return updateSpoolerRunTimestamp(instanceIDs) -} diff --git a/internal/eventstore/v1/spooler/spooler_test.go b/internal/eventstore/v1/spooler/spooler_test.go deleted file mode 100644 index 862d2aed3..000000000 --- a/internal/eventstore/v1/spooler/spooler_test.go +++ /dev/null @@ -1,514 +0,0 @@ -package spooler - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/golang/mock/gomock" - - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler/mock" - "github.com/zitadel/zitadel/internal/view/repository" -) - -var ( - testNow = time.Now() -) - -type testHandler struct { - cycleDuration time.Duration - processSleep time.Duration - processError error - queryError error - viewModel string - bulkLimit uint64 - maxErrCount int -} - -func (h *testHandler) AggregateTypes() []models.AggregateType { - return nil -} - -func (h *testHandler) CurrentSequence(ctx context.Context, instanceID string) (uint64, error) { - return 0, nil -} - -func (h *testHandler) Eventstore() v1.Eventstore { - return nil -} - -func (h *testHandler) ViewModel() string { - return h.viewModel -} - -func (h *testHandler) Subscription() *v1.Subscription { - return nil -} - -func (h *testHandler) EventQuery(ctx context.Context, instanceIDs []string) (*models.SearchQuery, error) { - if h.queryError != nil { - return nil, h.queryError - } - return &models.SearchQuery{}, nil -} - -func (h *testHandler) Reduce(*models.Event) error { - <-time.After(h.processSleep) - return h.processError -} - -func (h *testHandler) OnError(event *models.Event, err error) error { - if h.maxErrCount == 2 { - return nil - } - h.maxErrCount++ - return err -} - -func (h *testHandler) OnSuccess([]string) error { - return nil -} - -func (h *testHandler) MinimumCycleDuration() time.Duration { - return h.cycleDuration -} - -func (h *testHandler) LockDuration() time.Duration { - return h.cycleDuration / 2 -} - -func (h *testHandler) QueryLimit() uint64 { - return h.bulkLimit -} - -type eventstoreStub struct { - events []*models.Event - err error -} - -func (es *eventstoreStub) Subscribe(...models.AggregateType) *v1.Subscription { return nil } - -func (es *eventstoreStub) Health(ctx context.Context) error { - return nil -} - -func (es *eventstoreStub) AggregateCreator() *models.AggregateCreator { - return nil -} - -func (es *eventstoreStub) FilterEvents(ctx context.Context, in *models.SearchQuery) ([]*models.Event, error) { - if es.err != nil { - return nil, es.err - } - return es.events, nil -} -func (es *eventstoreStub) PushAggregates(ctx context.Context, in ...*models.Aggregate) error { - return nil -} - -func (es *eventstoreStub) LatestSequence(ctx context.Context, in *models.SearchQueryFactory) (uint64, error) { - return 0, nil -} -func (es *eventstoreStub) InstanceIDs(ctx context.Context, in *models.SearchQuery) ([]string, error) { - return nil, nil -} -func (es *eventstoreStub) V2() *eventstore.Eventstore { - return nil -} - -func TestSpooler_process(t *testing.T) { - type fields struct { - currentHandler *testHandler - } - type args struct { - timeout time.Duration - events []*models.Event - instanceIDs []string - } - tests := []struct { - name string - fields fields - args args - wantErr bool - wantRetries int - }{ - { - name: "process all events", - fields: fields{ - currentHandler: &testHandler{}, - }, - args: args{ - timeout: 0, - events: []*models.Event{{}, {}}, - }, - wantErr: false, - }, - { - name: "deadline exeeded", - fields: fields{ - currentHandler: &testHandler{processSleep: 501 * time.Millisecond}, - }, - args: args{ - timeout: 1 * time.Second, - events: []*models.Event{{}, {}, {}, {}}, - }, - wantErr: false, - }, - { - name: "process error", - fields: fields{ - currentHandler: &testHandler{processSleep: 1 * time.Second, processError: fmt.Errorf("i am an error")}, - }, - args: args{ - events: []*models.Event{{}, {}}, - }, - wantErr: false, - wantRetries: 2, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &spooledHandler{ - Handler: tt.fields.currentHandler, - } - - ctx := context.Background() - var start time.Time - if tt.args.timeout > 0 { - ctx, _ = context.WithTimeout(ctx, tt.args.timeout) - start = time.Now() - } - - if err := s.process(ctx, tt.args.events, "test", tt.args.instanceIDs); (err != nil) != tt.wantErr { - t.Errorf("Spooler.process() error = %v, wantErr %v", err, tt.wantErr) - } - if tt.fields.currentHandler.maxErrCount != tt.wantRetries { - t.Errorf("Spooler.process() wrong retry count got: %d want %d", tt.fields.currentHandler.maxErrCount, tt.wantRetries) - } - - elapsed := time.Since(start).Round(1 * time.Second) - if tt.args.timeout != 0 && elapsed != tt.args.timeout { - t.Errorf("wrong timeout wanted %v elapsed %v since %v", tt.args.timeout, elapsed, time.Since(start)) - } - }) - } -} - -func TestSpooler_awaitError(t *testing.T) { - type fields struct { - currentHandler query.Handler - err error - canceled bool - } - tests := []struct { - name string - fields fields - }{ - { - "no error", - fields{ - err: nil, - currentHandler: &testHandler{processSleep: 500 * time.Millisecond}, - canceled: false, - }, - }, - { - "with error", - fields{ - err: fmt.Errorf("hodor"), - currentHandler: &testHandler{processSleep: 500 * time.Millisecond}, - canceled: false, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := &spooledHandler{ - Handler: tt.fields.currentHandler, - } - c := make(chan interface{}) - errs := make(chan error) - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - s.awaitError(cancel, errs, "test") - c <- nil - }() - errs <- tt.fields.err - - <-c - if ctx.Err() == nil { - t.Error("cancel function was not called") - } - }) - } -} - -// TestSpooler_load checks if load terminates -func TestSpooler_load(t *testing.T) { - type fields struct { - currentHandler query.Handler - locker *testLocker - eventstore v1.Eventstore - } - tests := []struct { - name string - fields fields - }{ - { - "lock exists", - fields{ - currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView1", cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView1").expectRenew(t, fmt.Errorf("lock already exists"), 500*time.Millisecond), - }, - }, - { - "lock fails", - fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView2", cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView2").expectRenew(t, fmt.Errorf("fail"), 500*time.Millisecond), - eventstore: &eventstoreStub{events: []*models.Event{{}}}, - }, - }, - { - "query fails", - fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView3", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView3").expectRenew(t, nil, 500*time.Millisecond), - eventstore: &eventstoreStub{err: fmt.Errorf("fail")}, - }, - }, - { - "process event fails", - fields{ - currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView4", cycleDuration: 500 * time.Millisecond, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView4").expectRenew(t, nil, 250*time.Millisecond), - eventstore: &eventstoreStub{events: []*models.Event{{}}}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer tt.fields.locker.finish() - s := &spooledHandler{ - Handler: tt.fields.currentHandler, - locker: tt.fields.locker.mock, - eventstore: tt.fields.eventstore, - } - s.load("test-worker") - }) - } -} - -func TestSpooler_lock(t *testing.T) { - type fields struct { - currentHandler query.Handler - locker *testLocker - expectsErr bool - } - type args struct { - deadline time.Time - } - tests := []struct { - name string - fields fields - args args - }{ - { - "renew correct", - fields{ - currentHandler: &testHandler{cycleDuration: 1 * time.Second, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 500*time.Millisecond), - expectsErr: false, - }, - args{ - deadline: time.Now().Add(1 * time.Second), - }, - }, - { - "renew fails", - fields{ - currentHandler: &testHandler{cycleDuration: 900 * time.Millisecond, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 450*time.Millisecond), - expectsErr: true, - }, - args{ - deadline: time.Now().Add(5 * time.Second), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer tt.fields.locker.finish() - s := &spooledHandler{ - Handler: tt.fields.currentHandler, - locker: tt.fields.locker.mock, - } - - errs := make(chan error, 1) - defer close(errs) - ctx, _ := context.WithDeadline(context.Background(), tt.args.deadline) - - locked := s.lock(ctx, errs, "test-worker") - - if tt.fields.expectsErr { - lock := <-locked - err := <-errs - if err == nil { - t.Error("No error in error queue") - } - if lock { - t.Error("lock should have failed") - } - } else { - lock := <-locked - if !lock { - t.Error("lock should be true") - } - } - }) - } -} - -type testLocker struct { - mock *mock.MockLocker - lockerID string - viewName string - ctrl *gomock.Controller -} - -func newTestLocker(t *testing.T, lockerID, viewName string) *testLocker { - ctrl := gomock.NewController(t) - return &testLocker{mock.NewMockLocker(ctrl), lockerID, viewName, ctrl} -} - -func (l *testLocker) expectRenew(t *testing.T, err error, waitTime time.Duration) *testLocker { - t.Helper() - l.mock.EXPECT().Renew(gomock.Any(), l.viewName, gomock.Any(), gomock.Any()).DoAndReturn( - func(_, _, _ string, gotten time.Duration) error { - t.Helper() - if waitTime-gotten != 0 { - t.Errorf("expected waittime %v got %v", waitTime, gotten) - } - return err - }).MinTimes(1).MaxTimes(3) - - return l -} - -func (l *testLocker) finish() { - l.ctrl.Finish() -} - -func TestHandleError(t *testing.T) { - type args struct { - event *models.Event - failedErr error - latestFailedEvent func(sequence uint64, instanceID string) (*repository.FailedEvent, error) - errorCountUntilSkip uint64 - } - type res struct { - wantErr bool - shouldProcessSequence bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "should process sequence already too high", - args: args{ - event: &models.Event{Sequence: 30000000}, - failedErr: errors.ThrowInternal(nil, "SPOOL-Wk53B", "this was wrong"), - latestFailedEvent: func(s uint64, instanceID string) (*repository.FailedEvent, error) { - return &repository.FailedEvent{ - ErrMsg: "blub", - FailedSequence: s - 1, - FailureCount: 6, - ViewName: "super.table", - InstanceID: instanceID, - LastFailed: testNow, - }, nil - }, - errorCountUntilSkip: 5, - }, - res: res{ - shouldProcessSequence: true, - }, - }, - { - name: "should process sequence after this event too high", - args: args{ - event: &models.Event{Sequence: 30000000}, - failedErr: errors.ThrowInternal(nil, "SPOOL-Wk53B", "this was wrong"), - latestFailedEvent: func(s uint64, instanceID string) (*repository.FailedEvent, error) { - return &repository.FailedEvent{ - ErrMsg: "blub", - FailedSequence: s - 1, - FailureCount: 5, - ViewName: "super.table", - InstanceID: instanceID, - LastFailed: testNow, - }, nil - }, - errorCountUntilSkip: 6, - }, - res: res{ - shouldProcessSequence: true, - }, - }, - { - name: "should not process sequence", - args: args{ - event: &models.Event{Sequence: 30000000}, - failedErr: errors.ThrowInternal(nil, "SPOOL-Wk53B", "this was wrong"), - latestFailedEvent: func(s uint64, instanceID string) (*repository.FailedEvent, error) { - return &repository.FailedEvent{ - ErrMsg: "blub", - FailedSequence: s - 1, - FailureCount: 3, - ViewName: "super.table", - InstanceID: instanceID, - LastFailed: testNow, - }, nil - }, - errorCountUntilSkip: 5, - }, - res: res{ - shouldProcessSequence: false, - wantErr: true, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - processedSequence := false - err := HandleError( - tt.args.event, - tt.args.failedErr, - tt.args.latestFailedEvent, - func(*repository.FailedEvent) error { - return nil - }, - func(*models.Event) error { - processedSequence = true - return nil - }, - tt.args.errorCountUntilSkip) - - if (err != nil) != tt.res.wantErr { - t.Errorf("HandleError() error = %v, wantErr %v", err, tt.res.wantErr) - } - if tt.res.shouldProcessSequence != processedSequence { - t.Error("should not process sequence") - } - }) - } -} diff --git a/internal/eventstore/v1/subscription.go b/internal/eventstore/v1/subscription.go deleted file mode 100644 index 34280edf4..000000000 --- a/internal/eventstore/v1/subscription.go +++ /dev/null @@ -1,74 +0,0 @@ -package v1 - -import ( - "sync" - - "github.com/zitadel/zitadel/internal/eventstore/v1/models" -) - -var ( - subscriptions map[models.AggregateType][]*Subscription = map[models.AggregateType][]*Subscription{} - subsMutext sync.Mutex -) - -type Subscription struct { - Events chan *models.Event - aggregates []models.AggregateType -} - -func (es *eventstore) Subscribe(aggregates ...models.AggregateType) *Subscription { - events := make(chan *models.Event, 100) - sub := &Subscription{ - Events: events, - aggregates: aggregates, - } - - subsMutext.Lock() - defer subsMutext.Unlock() - - for _, aggregate := range aggregates { - _, ok := subscriptions[aggregate] - if !ok { - subscriptions[aggregate] = make([]*Subscription, 0, 1) - } - subscriptions[aggregate] = append(subscriptions[aggregate], sub) - } - - return sub -} - -func Notify(events []*models.Event) { - subsMutext.Lock() - defer subsMutext.Unlock() - for _, event := range events { - subs, ok := subscriptions[event.AggregateType] - if !ok { - continue - } - for _, sub := range subs { - sub.Events <- event - } - } -} - -func (s *Subscription) Unsubscribe() { - subsMutext.Lock() - defer subsMutext.Unlock() - for _, aggregate := range s.aggregates { - subs, ok := subscriptions[aggregate] - if !ok { - continue - } - for i := len(subs) - 1; i >= 0; i-- { - if subs[i] == s { - subs[i] = subs[len(subs)-1] - subs[len(subs)-1] = nil - subs = subs[:len(subs)-1] - } - } - } - _, ok := <-s.Events - if ok { - close(s.Events) - } -} diff --git a/internal/eventstore/v3/event.go b/internal/eventstore/v3/event.go new file mode 100644 index 000000000..f99e91b05 --- /dev/null +++ b/internal/eventstore/v3/event.go @@ -0,0 +1,107 @@ +package eventstore + +import ( + "encoding/json" + "time" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + _ eventstore.Event = (*event)(nil) +) + +type event struct { + aggregate *eventstore.Aggregate + creator string + revision uint16 + typ eventstore.EventType + createdAt time.Time + sequence uint64 + position float64 + payload Payload +} + +func commandToEvent(sequence *latestSequence, command eventstore.Command) (_ *event, err error) { + var payload Payload + if command.Payload() != nil { + payload, err = json.Marshal(command.Payload()) + if err != nil { + logging.WithError(err).Warn("marshal payload failed") + return nil, errors.ThrowInternal(err, "V3-MInPK", "Errors.Internal") + } + } + return &event{ + aggregate: sequence.aggregate, + creator: command.Creator(), + revision: command.Revision(), + typ: command.Type(), + payload: payload, + sequence: sequence.sequence, + }, nil +} + +// CreationDate implements [eventstore.Event] +func (e *event) CreationDate() time.Time { + return e.CreatedAt() +} + +// EditorUser implements [eventstore.Event] +func (e *event) EditorUser() string { + return e.Creator() +} + +// Aggregate implements [eventstore.Event] +func (e *event) Aggregate() *eventstore.Aggregate { + return e.aggregate +} + +// Creator implements [eventstore.Event] +func (e *event) Creator() string { + return e.creator +} + +// Revision implements [eventstore.Event] +func (e *event) Revision() uint16 { + return e.revision +} + +// Type implements [eventstore.Event] +func (e *event) Type() eventstore.EventType { + return e.typ +} + +// CreatedAt implements [eventstore.Event] +func (e *event) CreatedAt() time.Time { + return e.createdAt +} + +// Sequence implements [eventstore.Event] +func (e *event) Sequence() uint64 { + return e.sequence +} + +// Sequence implements [eventstore.Event] +func (e *event) Position() float64 { + return e.position +} + +// Unmarshal implements [eventstore.Event] +func (e *event) Unmarshal(ptr any) error { + if len(e.payload) == 0 { + return nil + } + if err := json.Unmarshal(e.payload, ptr); err != nil { + return errors.ThrowInternal(err, "V3-u8qVo", "Errors.Internal") + } + + return nil +} + +// DataAsBytes implements [eventstore.Event] +func (e *event) DataAsBytes() []byte { + return e.payload +} diff --git a/internal/eventstore/v3/event_test.go b/internal/eventstore/v3/event_test.go new file mode 100644 index 000000000..82a3aa1c9 --- /dev/null +++ b/internal/eventstore/v3/event_test.go @@ -0,0 +1,128 @@ +package eventstore + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/internal/eventstore" +) + +func Test_commandToEvent(t *testing.T) { + payload := struct { + ID string + }{ + ID: "test", + } + payloadMarshalled, err := json.Marshal(payload) + if err != nil { + t.Fatalf("marshal of payload failed: %v", err) + } + type args struct { + sequence *latestSequence + command eventstore.Command + } + type want struct { + event *event + err func(t *testing.T, err error) + } + tests := []struct { + name string + args args + want want + }{ + { + name: "no payload", + args: args{ + sequence: &latestSequence{ + aggregate: mockAggregate("V3-Red9I"), + sequence: 0, + }, + command: &mockCommand{ + aggregate: mockAggregate("V3-Red9I"), + payload: nil, + }, + }, + want: want{ + event: mockEvent( + mockAggregate("V3-Red9I"), + 0, + nil, + ).(*event), + }, + }, + { + name: "struct payload", + args: args{ + sequence: &latestSequence{ + aggregate: mockAggregate("V3-Red9I"), + sequence: 0, + }, + command: &mockCommand{ + aggregate: mockAggregate("V3-Red9I"), + payload: payload, + }, + }, + want: want{ + event: mockEvent( + mockAggregate("V3-Red9I"), + 0, + payloadMarshalled, + ).(*event), + }, + }, + { + name: "pointer payload", + args: args{ + sequence: &latestSequence{ + aggregate: mockAggregate("V3-Red9I"), + sequence: 0, + }, + command: &mockCommand{ + aggregate: mockAggregate("V3-Red9I"), + payload: &payload, + }, + }, + want: want{ + event: mockEvent( + mockAggregate("V3-Red9I"), + 0, + payloadMarshalled, + ).(*event), + }, + }, + { + name: "invalid payload", + args: args{ + sequence: &latestSequence{ + aggregate: mockAggregate("V3-Red9I"), + sequence: 0, + }, + command: &mockCommand{ + aggregate: mockAggregate("V3-Red9I"), + payload: func() {}, + }, + }, + want: want{ + err: func(t *testing.T, err error) { + assert.Error(t, err) + }, + }, + }, + } + for _, tt := range tests { + if tt.want.err == nil { + tt.want.err = func(t *testing.T, err error) { + require.NoError(t, err) + } + } + t.Run(tt.name, func(t *testing.T) { + got, err := commandToEvent(tt.args.sequence, tt.args.command) + + tt.want.err(t, err) + assert.Equal(t, tt.want.event, got) + }) + } +} diff --git a/internal/eventstore/v3/eventstore.go b/internal/eventstore/v3/eventstore.go new file mode 100644 index 000000000..829f6a940 --- /dev/null +++ b/internal/eventstore/v3/eventstore.go @@ -0,0 +1,29 @@ +package eventstore + +import ( + "context" + + "github.com/zitadel/zitadel/internal/database" +) + +// pushPlaceholderFmt defines how data are inserted into the events table +var pushPlaceholderFmt string + +type Eventstore struct { + client *database.DB +} + +func NewEventstore(client *database.DB) *Eventstore { + switch client.Type() { + case "cockroach": + pushPlaceholderFmt = "($%d, $%d, $%d, $%d, $%d, $%d, $%d, $%d, $%d, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $%d)" + case "postgres": + pushPlaceholderFmt = "($%d, $%d, $%d, $%d, $%d, $%d, $%d, $%d, $%d, statement_timestamp(), EXTRACT(EPOCH FROM clock_timestamp()), $%d)" + } + + return &Eventstore{client: client} +} + +func (es *Eventstore) Health(ctx context.Context) error { + return es.client.PingContext(ctx) +} diff --git a/internal/eventstore/v3/mock_test.go b/internal/eventstore/v3/mock_test.go new file mode 100644 index 000000000..42435d9c4 --- /dev/null +++ b/internal/eventstore/v3/mock_test.go @@ -0,0 +1,64 @@ +package eventstore + +import ( + "github.com/zitadel/zitadel/internal/eventstore" +) + +var _ eventstore.Command = (*mockCommand)(nil) + +type mockCommand struct { + aggregate *eventstore.Aggregate + payload any + constraints []*eventstore.UniqueConstraint +} + +// Aggregate implements [eventstore.Command] +func (m *mockCommand) Aggregate() *eventstore.Aggregate { + return m.aggregate +} + +// Creator implements [eventstore.Command] +func (m *mockCommand) Creator() string { + return "creator" +} + +// Revision implements [eventstore.Command] +func (m *mockCommand) Revision() uint16 { + return 1 +} + +// Type implements [eventstore.Command] +func (m *mockCommand) Type() eventstore.EventType { + return "event.type" +} + +// Payload implements [eventstore.Command] +func (m *mockCommand) Payload() any { + return m.payload +} + +// UniqueConstraints implements [eventstore.Command] +func (m *mockCommand) UniqueConstraints() []*eventstore.UniqueConstraint { + return m.constraints +} + +func mockEvent(aggregate *eventstore.Aggregate, sequence uint64, payload Payload) eventstore.Event { + return &event{ + aggregate: aggregate, + creator: "creator", + revision: 1, + typ: "event.type", + sequence: sequence, + payload: payload, + } +} + +func mockAggregate(id string) *eventstore.Aggregate { + return &eventstore.Aggregate{ + ID: id, + Type: "type", + ResourceOwner: "ro", + InstanceID: "instance", + Version: "v1", + } +} diff --git a/internal/eventstore/v3/push.go b/internal/eventstore/v3/push.go new file mode 100644 index 000000000..b954b5ca4 --- /dev/null +++ b/internal/eventstore/v3/push.go @@ -0,0 +1,171 @@ +package eventstore + +import ( + "context" + "database/sql" + _ "embed" + errs "errors" + "fmt" + "strconv" + "strings" + "sync" + + "github.com/cockroachdb/cockroach-go/v2/crdb" + "github.com/jackc/pgconn" + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +func (es *Eventstore) Push(ctx context.Context, commands ...eventstore.Command) (events []eventstore.Event, err error) { + tx, err := es.client.Begin() + if err != nil { + return nil, err + } + // tx is not closed because [crdb.ExecuteInTx] takes care of that + var ( + sequences []*latestSequence + once sync.Once + ) + + err = crdb.ExecuteInTx(ctx, &transaction{tx}, func() error { + once.Do(func() { + sequences, err = latestSequences(ctx, tx, commands) + }) + if err != nil { + return err + } + + events, err = insertEvents(ctx, tx, sequences, commands) + if err != nil { + return err + } + + return handleUniqueConstraints(ctx, tx, commands) + }) + + if err != nil { + return nil, err + } + + return events, nil +} + +//go:embed push.sql +var pushStmt string + +func insertEvents(ctx context.Context, tx *sql.Tx, sequences []*latestSequence, commands []eventstore.Command) ([]eventstore.Event, error) { + events, placeholders, args, err := mapCommands(commands, sequences) + if err != nil { + return nil, err + } + + rows, err := tx.QueryContext(ctx, fmt.Sprintf(pushStmt, strings.Join(placeholders, ", ")), args...) + if err != nil { + return nil, err + } + defer rows.Close() + + for i := 0; rows.Next(); i++ { + err = rows.Scan(&events[i].(*event).createdAt, &events[i].(*event).position) + if err != nil { + logging.WithError(err).Warn("failed to scan events") + return nil, err + } + } + + if err := rows.Err(); err != nil { + pgErr := new(pgconn.PgError) + if errs.As(err, &pgErr) { + // Check if push tries to write an event just written + // by another transaction + if pgErr.Code == "40001" { + // TODO: @livio-a should we return the parent or not? + return nil, errors.ThrowInvalidArgument(err, "V3-p5xAn", "Errors.AlreadyExists") + } + } + logging.WithError(rows.Err()).Warn("failed to push events") + return nil, errors.ThrowInternal(err, "V3-VGnZY", "Errors.Internal") + } + + return events, nil +} + +const argsPerCommand = 10 + +func mapCommands(commands []eventstore.Command, sequences []*latestSequence) (events []eventstore.Event, placeholders []string, args []any, err error) { + events = make([]eventstore.Event, len(commands)) + args = make([]any, 0, len(commands)*argsPerCommand) + placeholders = make([]string, len(commands)) + + for i, command := range commands { + sequence := searchSequenceByCommand(sequences, command) + if sequence == nil { + logging.WithFields( + "aggType", command.Aggregate().Type, + "aggID", command.Aggregate().ID, + "instance", command.Aggregate().InstanceID, + ).Panic("no sequence found") + // added return for linting + return nil, nil, nil, nil + } + sequence.sequence++ + + events[i], err = commandToEvent(sequence, command) + if err != nil { + return nil, nil, nil, err + } + + placeholders[i] = fmt.Sprintf(pushPlaceholderFmt, + i*argsPerCommand+1, + i*argsPerCommand+2, + i*argsPerCommand+3, + i*argsPerCommand+4, + i*argsPerCommand+5, + i*argsPerCommand+6, + i*argsPerCommand+7, + i*argsPerCommand+8, + i*argsPerCommand+9, + i*argsPerCommand+10, + ) + + revision, err := strconv.Atoi(strings.TrimPrefix(string(events[i].(*event).aggregate.Version), "v")) + if err != nil { + return nil, nil, nil, errors.ThrowInternal(err, "V3-JoZEp", "Errors.Internal") + } + args = append(args, + events[i].(*event).aggregate.InstanceID, + events[i].(*event).aggregate.ResourceOwner, + events[i].(*event).aggregate.Type, + events[i].(*event).aggregate.ID, + revision, + events[i].(*event).creator, + events[i].(*event).typ, + events[i].(*event).payload, + events[i].(*event).sequence, + i, + ) + } + + return events, placeholders, args, nil +} + +type transaction struct { + *sql.Tx +} + +var _ crdb.Tx = (*transaction)(nil) + +func (t *transaction) Exec(ctx context.Context, query string, args ...interface{}) error { + _, err := t.Tx.ExecContext(ctx, query, args...) + return err +} + +func (t *transaction) Commit(ctx context.Context) error { + return t.Tx.Commit() +} + +func (t *transaction) Rollback(ctx context.Context) error { + return t.Tx.Rollback() +} diff --git a/internal/eventstore/v3/push.sql b/internal/eventstore/v3/push.sql new file mode 100644 index 000000000..025094895 --- /dev/null +++ b/internal/eventstore/v3/push.sql @@ -0,0 +1,18 @@ +INSERT INTO eventstore.events2 ( + instance_id + , "owner" + , aggregate_type + , aggregate_id + , revision + + , creator + , event_type + , payload + , "sequence" + , created_at + + , "position" + , in_tx_order +) VALUES + %s +RETURNING created_at, "position"; \ No newline at end of file diff --git a/internal/eventstore/v3/push_test.go b/internal/eventstore/v3/push_test.go new file mode 100644 index 000000000..0f16a2ac7 --- /dev/null +++ b/internal/eventstore/v3/push_test.go @@ -0,0 +1,253 @@ +package eventstore + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/database/cockroach" + "github.com/zitadel/zitadel/internal/eventstore" +) + +func Test_mapCommands(t *testing.T) { + type args struct { + commands []eventstore.Command + sequences []*latestSequence + } + type want struct { + events []eventstore.Event + placeHolders []string + args []any + err func(t *testing.T, err error) + shouldPanic bool + } + tests := []struct { + name string + args args + want want + }{ + { + name: "no commands", + args: args{ + commands: []eventstore.Command{}, + sequences: []*latestSequence{}, + }, + want: want{ + events: []eventstore.Event{}, + placeHolders: []string{}, + args: []any{}, + }, + }, + { + name: "one command", + args: args{ + commands: []eventstore.Command{ + &mockCommand{ + aggregate: mockAggregate("V3-VEIvq"), + }, + }, + sequences: []*latestSequence{ + { + aggregate: mockAggregate("V3-VEIvq"), + sequence: 0, + }, + }, + }, + want: want{ + events: []eventstore.Event{ + mockEvent( + mockAggregate("V3-VEIvq"), + 1, + nil, + ), + }, + placeHolders: []string{ + "($1, $2, $3, $4, $5, $6, $7, $8, $9, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $10)", + }, + args: []any{ + "instance", + "ro", + eventstore.AggregateType("type"), + "V3-VEIvq", + 1, + "creator", + eventstore.EventType("event.type"), + Payload(nil), + uint64(1), + 0, + }, + err: func(t *testing.T, err error) {}, + }, + }, + { + name: "multiple commands same aggregate", + args: args{ + commands: []eventstore.Command{ + &mockCommand{ + aggregate: mockAggregate("V3-VEIvq"), + }, + &mockCommand{ + aggregate: mockAggregate("V3-VEIvq"), + }, + }, + sequences: []*latestSequence{ + { + aggregate: mockAggregate("V3-VEIvq"), + sequence: 5, + }, + }, + }, + want: want{ + events: []eventstore.Event{ + mockEvent( + mockAggregate("V3-VEIvq"), + 6, + nil, + ), + mockEvent( + mockAggregate("V3-VEIvq"), + 7, + nil, + ), + }, + placeHolders: []string{ + "($1, $2, $3, $4, $5, $6, $7, $8, $9, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $10)", + "($11, $12, $13, $14, $15, $16, $17, $18, $19, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $20)", + }, + args: []any{ + // first event + "instance", + "ro", + eventstore.AggregateType("type"), + "V3-VEIvq", + 1, + "creator", + eventstore.EventType("event.type"), + Payload(nil), + uint64(6), + 0, + // second event + "instance", + "ro", + eventstore.AggregateType("type"), + "V3-VEIvq", + 1, + "creator", + eventstore.EventType("event.type"), + Payload(nil), + uint64(7), + 1, + }, + err: func(t *testing.T, err error) {}, + }, + }, + { + name: "one command per aggregate", + args: args{ + commands: []eventstore.Command{ + &mockCommand{ + aggregate: mockAggregate("V3-VEIvq"), + }, + &mockCommand{ + aggregate: mockAggregate("V3-IT6VN"), + }, + }, + sequences: []*latestSequence{ + { + aggregate: mockAggregate("V3-VEIvq"), + sequence: 5, + }, + { + aggregate: mockAggregate("V3-IT6VN"), + sequence: 0, + }, + }, + }, + want: want{ + events: []eventstore.Event{ + mockEvent( + mockAggregate("V3-VEIvq"), + 6, + nil, + ), + mockEvent( + mockAggregate("V3-IT6VN"), + 1, + nil, + ), + }, + placeHolders: []string{ + "($1, $2, $3, $4, $5, $6, $7, $8, $9, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $10)", + "($11, $12, $13, $14, $15, $16, $17, $18, $19, hlc_to_timestamp(cluster_logical_timestamp()), cluster_logical_timestamp(), $20)", + }, + args: []any{ + // first event + "instance", + "ro", + eventstore.AggregateType("type"), + "V3-VEIvq", + 1, + "creator", + eventstore.EventType("event.type"), + Payload(nil), + uint64(6), + 0, + // second event + "instance", + "ro", + eventstore.AggregateType("type"), + "V3-IT6VN", + 1, + "creator", + eventstore.EventType("event.type"), + Payload(nil), + uint64(1), + 1, + }, + err: func(t *testing.T, err error) {}, + }, + }, + { + name: "missing sequence", + args: args{ + commands: []eventstore.Command{ + &mockCommand{ + aggregate: mockAggregate("V3-VEIvq"), + }, + }, + sequences: []*latestSequence{}, + }, + want: want{ + events: []eventstore.Event{}, + placeHolders: []string{}, + args: []any{}, + err: func(t *testing.T, err error) {}, + shouldPanic: true, + }, + }, + } + for _, tt := range tests { + if tt.want.err == nil { + tt.want.err = func(t *testing.T, err error) { + require.NoError(t, err) + } + } + // is used to set the the [pushPlaceholderFmt] + NewEventstore(&database.DB{Database: new(cockroach.Config)}) + t.Run(tt.name, func(t *testing.T) { + defer func() { + cause := recover() + assert.Equal(t, tt.want.shouldPanic, cause != nil) + }() + gotEvents, gotPlaceHolders, gotArgs, err := mapCommands(tt.args.commands, tt.args.sequences) + tt.want.err(t, err) + + assert.ElementsMatch(t, tt.want.events, gotEvents) + assert.ElementsMatch(t, tt.want.placeHolders, gotPlaceHolders) + assert.ElementsMatch(t, tt.want.args, gotArgs) + }) + } +} diff --git a/internal/eventstore/v3/sequence.go b/internal/eventstore/v3/sequence.go new file mode 100644 index 000000000..3155ac3a9 --- /dev/null +++ b/internal/eventstore/v3/sequence.go @@ -0,0 +1,132 @@ +package eventstore + +import ( + "context" + "database/sql" + _ "embed" + "fmt" + "strings" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +type latestSequence struct { + aggregate *eventstore.Aggregate + sequence uint64 +} + +//go:embed sequences_query.sql +var latestSequencesStmt string + +func latestSequences(ctx context.Context, tx *sql.Tx, commands []eventstore.Command) ([]*latestSequence, error) { + sequences := commandsToSequences(ctx, commands) + + conditions, args := sequencesToSql(sequences) + rows, err := tx.QueryContext(ctx, fmt.Sprintf(latestSequencesStmt, strings.Join(conditions, " OR ")), args...) + if err != nil { + return nil, errors.ThrowInternal(err, "V3-5jU5z", "Errors.Internal") + } + defer rows.Close() + + for rows.Next() { + if err := scanToSequence(rows, sequences); err != nil { + return nil, errors.ThrowInternal(err, "V3-Ydiwv", "Errors.Internal") + } + } + + if rows.Err() != nil { + return nil, errors.ThrowInternal(rows.Err(), "V3-XApDk", "Errors.Internal") + } + return sequences, nil +} + +func searchSequenceByCommand(sequences []*latestSequence, command eventstore.Command) *latestSequence { + for _, sequence := range sequences { + if sequence.aggregate.Type == command.Aggregate().Type && + sequence.aggregate.ID == command.Aggregate().ID && + sequence.aggregate.InstanceID == command.Aggregate().InstanceID { + return sequence + } + } + return nil +} + +func searchSequence(sequences []*latestSequence, aggregateType eventstore.AggregateType, aggregateID, instanceID string) *latestSequence { + for _, sequence := range sequences { + if sequence.aggregate.Type == aggregateType && + sequence.aggregate.ID == aggregateID && + sequence.aggregate.InstanceID == instanceID { + return sequence + } + } + return nil +} + +func commandsToSequences(ctx context.Context, commands []eventstore.Command) []*latestSequence { + sequences := make([]*latestSequence, 0, len(commands)) + + for _, command := range commands { + if searchSequenceByCommand(sequences, command) != nil { + continue + } + + if command.Aggregate().InstanceID == "" { + command.Aggregate().InstanceID = authz.GetInstance(ctx).InstanceID() + } + sequences = append(sequences, &latestSequence{ + aggregate: command.Aggregate(), + }) + } + + return sequences +} + +const argsPerCondition = 3 + +func sequencesToSql(sequences []*latestSequence) (conditions []string, args []any) { + args = make([]interface{}, 0, len(sequences)*argsPerCondition) + conditions = make([]string, len(sequences)) + + for i, sequence := range sequences { + conditions[i] = fmt.Sprintf("(instance_id = $%d AND aggregate_type = $%d AND aggregate_id = $%d)", + i*argsPerCondition+1, + i*argsPerCondition+2, + i*argsPerCondition+3, + ) + args = append(args, sequence.aggregate.InstanceID, sequence.aggregate.Type, sequence.aggregate.ID) + } + + return conditions, args +} + +func scanToSequence(rows *sql.Rows, sequences []*latestSequence) error { + var aggregateType eventstore.AggregateType + var aggregateID, instanceID string + var currentSequence uint64 + var resourceOwner string + + if err := rows.Scan(&instanceID, &resourceOwner, &aggregateType, &aggregateID, ¤tSequence); err != nil { + return errors.ThrowInternal(err, "V3-OIWqj", "Errors.Internal") + } + + sequence := searchSequence(sequences, aggregateType, aggregateID, instanceID) + if sequence == nil { + logging.WithFields( + "aggType", aggregateType, + "aggID", aggregateID, + "instance", instanceID, + ).Panic("no sequence found") + // added return for linting + return nil + } + sequence.sequence = currentSequence + if sequence.aggregate.ResourceOwner == "" { + sequence.aggregate.ResourceOwner = resourceOwner + } + + return nil +} diff --git a/internal/eventstore/v3/sequence_test.go b/internal/eventstore/v3/sequence_test.go new file mode 100644 index 000000000..55ee73831 --- /dev/null +++ b/internal/eventstore/v3/sequence_test.go @@ -0,0 +1,293 @@ +package eventstore + +import ( + "context" + _ "embed" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/eventstore" +) + +func Test_searchSequence(t *testing.T) { + sequence := &latestSequence{ + aggregate: mockAggregate("V3-p1BWC"), + sequence: 1, + } + type args struct { + sequences []*latestSequence + aggregateType eventstore.AggregateType + aggregateID string + instanceID string + } + tests := []struct { + name string + args args + want *latestSequence + }{ + { + name: "type missmatch", + args: args{ + sequences: []*latestSequence{ + sequence, + }, + aggregateType: "wrong", + aggregateID: "V3-p1BWC", + instanceID: "instance", + }, + want: nil, + }, + { + name: "id missmatch", + args: args{ + sequences: []*latestSequence{ + sequence, + }, + aggregateType: "type", + aggregateID: "wrong", + instanceID: "instance", + }, + want: nil, + }, + { + name: "instance missmatch", + args: args{ + sequences: []*latestSequence{ + sequence, + }, + aggregateType: "type", + aggregateID: "V3-p1BWC", + instanceID: "wrong", + }, + want: nil, + }, + { + name: "match", + args: args{ + sequences: []*latestSequence{ + sequence, + }, + aggregateType: "type", + aggregateID: "V3-p1BWC", + instanceID: "instance", + }, + want: sequence, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := searchSequence(tt.args.sequences, tt.args.aggregateType, tt.args.aggregateID, tt.args.instanceID); !reflect.DeepEqual(got, tt.want) { + t.Errorf("searchSequence() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_commandsToSequences(t *testing.T) { + aggregate := mockAggregate("V3-MKHTF") + type args struct { + ctx context.Context + commands []eventstore.Command + } + tests := []struct { + name string + args args + want []*latestSequence + }{ + { + name: "no command", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{}, + }, + want: []*latestSequence{}, + }, + { + name: "one command", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + &mockCommand{ + aggregate: aggregate, + }, + }, + }, + want: []*latestSequence{ + { + aggregate: aggregate, + }, + }, + }, + { + name: "two commands same aggregate", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + &mockCommand{ + aggregate: aggregate, + }, + &mockCommand{ + aggregate: aggregate, + }, + }, + }, + want: []*latestSequence{ + { + aggregate: aggregate, + }, + }, + }, + { + name: "two commands different aggregates", + args: args{ + ctx: context.Background(), + commands: []eventstore.Command{ + &mockCommand{ + aggregate: aggregate, + }, + &mockCommand{ + aggregate: mockAggregate("V3-cZkCy"), + }, + }, + }, + want: []*latestSequence{ + { + aggregate: aggregate, + }, + { + aggregate: mockAggregate("V3-cZkCy"), + }, + }, + }, + { + name: "instance set in command", + args: args{ + ctx: authz.WithInstanceID(context.Background(), "V3-ANV4p"), + commands: []eventstore.Command{ + &mockCommand{ + aggregate: &eventstore.Aggregate{ + ID: "V3-bF0Sa", + Type: "type", + ResourceOwner: "to", + InstanceID: "instance", + Version: "v1", + }, + }, + }, + }, + want: []*latestSequence{ + { + aggregate: &eventstore.Aggregate{ + ID: "V3-bF0Sa", + Type: "type", + ResourceOwner: "to", + InstanceID: "instance", + Version: "v1", + }, + }, + }, + }, + { + name: "instance from context", + args: args{ + ctx: authz.WithInstanceID(context.Background(), "V3-ANV4p"), + commands: []eventstore.Command{ + &mockCommand{ + aggregate: &eventstore.Aggregate{ + ID: "V3-bF0Sa", + Type: "type", + ResourceOwner: "to", + Version: "v1", + }, + }, + }, + }, + want: []*latestSequence{ + { + aggregate: &eventstore.Aggregate{ + ID: "V3-bF0Sa", + Type: "type", + ResourceOwner: "to", + InstanceID: "V3-ANV4p", + Version: "v1", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := commandsToSequences(tt.args.ctx, tt.args.commands) + assert.ElementsMatch(t, tt.want, got) + }) + } +} + +func Test_sequencesToSql(t *testing.T) { + tests := []struct { + name string + arg []*latestSequence + wantConditions []string + wantArgs []any + }{ + { + name: "no sequence", + arg: []*latestSequence{}, + wantConditions: []string{}, + wantArgs: []any{}, + }, + { + name: "one", + arg: []*latestSequence{ + { + aggregate: mockAggregate("V3-SbpGB"), + }, + }, + wantConditions: []string{ + "(instance_id = $1 AND aggregate_type = $2 AND aggregate_id = $3)", + }, + wantArgs: []any{ + "instance", + eventstore.AggregateType("type"), + "V3-SbpGB", + }, + }, + { + name: "multiple", + arg: []*latestSequence{ + { + aggregate: mockAggregate("V3-SbpGB"), + }, + { + aggregate: mockAggregate("V3-0X3yt"), + }, + }, + wantConditions: []string{ + "(instance_id = $1 AND aggregate_type = $2 AND aggregate_id = $3)", + "(instance_id = $4 AND aggregate_type = $5 AND aggregate_id = $6)", + }, + wantArgs: []any{ + "instance", + eventstore.AggregateType("type"), + "V3-SbpGB", + "instance", + eventstore.AggregateType("type"), + "V3-0X3yt", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotConditions, gotArgs := sequencesToSql(tt.arg) + if !reflect.DeepEqual(gotConditions, tt.wantConditions) { + t.Errorf("sequencesToSql() gotConditions = %v, want %v", gotConditions, tt.wantConditions) + } + if !reflect.DeepEqual(gotArgs, tt.wantArgs) { + t.Errorf("sequencesToSql() gotArgs = %v, want %v", gotArgs, tt.wantArgs) + } + }) + } +} diff --git a/internal/eventstore/v3/sequences_query.sql b/internal/eventstore/v3/sequences_query.sql new file mode 100644 index 000000000..fb164013d --- /dev/null +++ b/internal/eventstore/v3/sequences_query.sql @@ -0,0 +1,30 @@ +with existing as ( + SELECT + instance_id + , aggregate_type + , aggregate_id + , MAX("sequence") "sequence" + FROM + eventstore.events2 existing + WHERE + %s + GROUP BY + instance_id + , aggregate_type + , aggregate_id +) SELECT + e.instance_id + , e.owner + , e.aggregate_type + , e.aggregate_id + , e.sequence +FROM + eventstore.events2 e +JOIN + existing +ON + e.instance_id = existing.instance_id + AND e.aggregate_type = existing.aggregate_type + AND e.aggregate_id = existing.aggregate_id + AND e.sequence = existing.sequence +FOR UPDATE; \ No newline at end of file diff --git a/internal/eventstore/v3/type.go b/internal/eventstore/v3/type.go new file mode 100644 index 000000000..3ed807b44 --- /dev/null +++ b/internal/eventstore/v3/type.go @@ -0,0 +1,25 @@ +package eventstore + +import "database/sql/driver" + +// Payload represents a byte array that may be null. +// Payload implements the sql.Scanner interface +type Payload []byte + +// Scan implements the Scanner interface. +func (data *Payload) Scan(value interface{}) error { + if value == nil { + *data = nil + return nil + } + *data = Payload(value.([]byte)) + return nil +} + +// Value implements the driver Valuer interface. +func (data Payload) Value() (driver.Value, error) { + if len(data) == 0 { + return nil, nil + } + return []byte(data), nil +} diff --git a/internal/eventstore/v3/unique_constraints.go b/internal/eventstore/v3/unique_constraints.go new file mode 100644 index 000000000..0527f1687 --- /dev/null +++ b/internal/eventstore/v3/unique_constraints.go @@ -0,0 +1,89 @@ +package eventstore + +import ( + "context" + "database/sql" + _ "embed" + "errors" + "fmt" + "strings" + + "github.com/jackc/pgconn" + "github.com/zitadel/logging" + + errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed unique_constraints_delete.sql + deleteConstraintStmt string + //go:embed unique_constraints_add.sql + addConstraintStmt string +) + +func handleUniqueConstraints(ctx context.Context, tx *sql.Tx, commands []eventstore.Command) error { + deletePlaceholders := make([]string, 0) + deleteArgs := make([]any, 0) + + addPlaceholders := make([]string, 0) + addArgs := make([]any, 0) + addConstraints := map[string]*eventstore.UniqueConstraint{} + deleteConstraints := map[string]*eventstore.UniqueConstraint{} + + for _, command := range commands { + for _, constraint := range command.UniqueConstraints() { + switch constraint.Action { + case eventstore.UniqueConstraintAdd: + addPlaceholders = append(addPlaceholders, fmt.Sprintf("($%d, $%d, $%d)", len(addArgs)+1, len(addArgs)+2, len(addArgs)+3)) + addArgs = append(addArgs, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField) + addConstraints[fmt.Sprintf("('%s', '%s', '%s')", command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + case eventstore.UniqueConstraintRemove: + deletePlaceholders = append(deletePlaceholders, fmt.Sprintf("(instance_id = $%d AND unique_type = $%d AND unique_field = $%d)", len(deleteArgs)+1, len(deleteArgs)+2, len(deleteArgs)+3)) + deleteArgs = append(deleteArgs, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField) + deleteConstraints[fmt.Sprintf("('%s', '%s', '%s')", command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + case eventstore.UniqueConstraintInstanceRemove: + deletePlaceholders = append(deletePlaceholders, fmt.Sprintf("(instance_id = $%d)", len(deleteArgs)+1)) + deleteArgs = append(deleteArgs, command.Aggregate().InstanceID) + deleteConstraints[fmt.Sprintf("('%s', '%s', '%s')", command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + } + } + } + + if len(deletePlaceholders) > 0 { + _, err := tx.ExecContext(ctx, fmt.Sprintf(deleteConstraintStmt, strings.Join(deletePlaceholders, " OR ")), deleteArgs...) + if err != nil { + logging.WithError(err).Warn("delete unique constraint failed") + errMessage := "Errors.Internal" + if constraint := constraintFromErr(err, deleteConstraints); constraint != nil { + errMessage = constraint.ErrorMessage + } + return errs.ThrowInternal(err, "V3-C8l3V", errMessage) + } + } + if len(addPlaceholders) > 0 { + _, err := tx.ExecContext(ctx, fmt.Sprintf(addConstraintStmt, strings.Join(addPlaceholders, ", ")), addArgs...) + if err != nil { + logging.WithError(err).Warn("add unique constraint failed") + errMessage := "Errors.Internal" + if constraint := constraintFromErr(err, addConstraints); constraint != nil { + errMessage = constraint.ErrorMessage + } + return errs.ThrowInternal(err, "V3-DKcYh", errMessage) + } + } + return nil +} + +func constraintFromErr(err error, constraints map[string]*eventstore.UniqueConstraint) *eventstore.UniqueConstraint { + pgErr := new(pgconn.PgError) + if !errors.As(err, &pgErr) { + return nil + } + for key, constraint := range constraints { + if strings.Contains(pgErr.Detail, key) { + return constraint + } + } + return nil +} diff --git a/internal/eventstore/v3/unique_constraints_add.sql b/internal/eventstore/v3/unique_constraints_add.sql new file mode 100644 index 000000000..1d0d9e5be --- /dev/null +++ b/internal/eventstore/v3/unique_constraints_add.sql @@ -0,0 +1,6 @@ +INSERT INTO eventstore.unique_constraints ( + instance_id + , unique_type + , unique_field +) VALUES + %s \ No newline at end of file diff --git a/internal/eventstore/v3/unique_constraints_delete.sql b/internal/eventstore/v3/unique_constraints_delete.sql new file mode 100644 index 000000000..f84b8c226 --- /dev/null +++ b/internal/eventstore/v3/unique_constraints_delete.sql @@ -0,0 +1 @@ +DELETE FROM eventstore.unique_constraints WHERE %s \ No newline at end of file diff --git a/internal/eventstore/version.go b/internal/eventstore/version.go index ee1c5b6ef..518b78d15 100644 --- a/internal/eventstore/version.go +++ b/internal/eventstore/version.go @@ -1,7 +1,18 @@ package eventstore import ( - "github.com/zitadel/zitadel/internal/eventstore/repository" + "regexp" + + "github.com/zitadel/zitadel/internal/errors" ) -type Version repository.Version +type Version string + +var versionRegexp = regexp.MustCompile(`^v[0-9]+(\.[0-9]+){0,2}$`) + +func (v Version) Validate() error { + if !versionRegexp.MatchString(string(v)) { + return errors.ThrowPreconditionFailed(nil, "MODEL-luDuS", "version is not semver") + } + return nil +} diff --git a/internal/eventstore/repository/version_test.go b/internal/eventstore/version_test.go similarity index 96% rename from internal/eventstore/repository/version_test.go rename to internal/eventstore/version_test.go index c64db50c3..5bc92f9d1 100644 --- a/internal/eventstore/repository/version_test.go +++ b/internal/eventstore/version_test.go @@ -1,4 +1,4 @@ -package repository +package eventstore import "testing" diff --git a/internal/eventstore/write_model.go b/internal/eventstore/write_model.go index 0ac28eb88..277e65ed8 100644 --- a/internal/eventstore/write_model.go +++ b/internal/eventstore/write_model.go @@ -2,7 +2,7 @@ package eventstore import "time" -//WriteModel is the minimum representation of a command side write model. +// WriteModel is the minimum representation of a command side write model. // It implements a basic reducer // it's purpose is to reduce events to create new ones type WriteModel struct { @@ -14,13 +14,13 @@ type WriteModel struct { ChangeDate time.Time `json:"-"` } -//AppendEvents adds all the events to the read model. +// AppendEvents adds all the events to the read model. // The function doesn't compute the new state of the read model func (rm *WriteModel) AppendEvents(events ...Event) { rm.Events = append(rm.Events, events...) } -//Reduce is the basic implementaion of reducer +// Reduce is the basic implementation of reducer // If this function is extended the extending function should be the last step func (wm *WriteModel) Reduce() error { if len(wm.Events) == 0 { @@ -38,7 +38,7 @@ func (wm *WriteModel) Reduce() error { } wm.ProcessedSequence = wm.Events[len(wm.Events)-1].Sequence() - wm.ChangeDate = wm.Events[len(wm.Events)-1].CreationDate() + wm.ChangeDate = wm.Events[len(wm.Events)-1].CreatedAt() // all events processed and not needed anymore wm.Events = nil diff --git a/internal/iam/repository/eventsourcing/model/org_iam_policy.go b/internal/iam/repository/eventsourcing/model/org_iam_policy.go index 7307de46b..0ad162465 100644 --- a/internal/iam/repository/eventsourcing/model/org_iam_policy.go +++ b/internal/iam/repository/eventsourcing/model/org_iam_policy.go @@ -1,9 +1,8 @@ package model import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" iam_model "github.com/zitadel/zitadel/internal/iam/model" ) @@ -32,8 +31,8 @@ func (p *DomainPolicy) Changes(changed *DomainPolicy) map[string]interface{} { return changes } -func (p *DomainPolicy) SetData(event *es_models.Event) error { - err := json.Unmarshal(event.Data, p) +func (p *DomainPolicy) SetData(event eventstore.Event) error { + err := event.Unmarshal(p) if err != nil { return errors.ThrowInternal(err, "EVENT-7JS9d", "unable to unmarshal data") } diff --git a/internal/iam/repository/view/model/idp_config.go b/internal/iam/repository/view/model/idp_config.go index a12745883..b2c41d93c 100644 --- a/internal/iam/repository/view/model/idp_config.go +++ b/internal/iam/repository/view/model/idp_config.go @@ -1,20 +1,17 @@ package model import ( - "encoding/json" "time" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" - "github.com/zitadel/zitadel/internal/repository/org" - "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/iam/model" + "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/org" ) const ( @@ -37,18 +34,18 @@ type IDPConfigView struct { IDPProviderType int32 `json:"-" gorm:"column:idp_provider_type"` AutoRegister bool `json:"autoRegister" gorm:"column:auto_register"` - IsOIDC bool `json:"-" gorm:"column:is_oidc"` - OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` - OIDCClientSecret *crypto.CryptoValue `json:"clientSecret" gorm:"column:oidc_client_secret"` - OIDCIssuer string `json:"issuer" gorm:"column:oidc_issuer"` - OIDCScopes database.StringArray `json:"scopes" gorm:"column:oidc_scopes"` - OIDCIDPDisplayNameMapping int32 `json:"idpDisplayNameMapping" gorm:"column:oidc_idp_display_name_mapping"` - OIDCUsernameMapping int32 `json:"usernameMapping" gorm:"column:oidc_idp_username_mapping"` - OAuthAuthorizationEndpoint string `json:"authorizationEndpoint" gorm:"column:oauth_authorization_endpoint"` - OAuthTokenEndpoint string `json:"tokenEndpoint" gorm:"column:oauth_token_endpoint"` - JWTEndpoint string `json:"jwtEndpoint" gorm:"jwt_endpoint"` - JWTKeysEndpoint string `json:"keysEndpoint" gorm:"jwt_keys_endpoint"` - JWTHeaderName string `json:"headerName" gorm:"jwt_header_name"` + IsOIDC bool `json:"-" gorm:"column:is_oidc"` + OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` + OIDCClientSecret *crypto.CryptoValue `json:"clientSecret" gorm:"column:oidc_client_secret"` + OIDCIssuer string `json:"issuer" gorm:"column:oidc_issuer"` + OIDCScopes database.TextArray[string] `json:"scopes" gorm:"column:oidc_scopes"` + OIDCIDPDisplayNameMapping int32 `json:"idpDisplayNameMapping" gorm:"column:oidc_idp_display_name_mapping"` + OIDCUsernameMapping int32 `json:"usernameMapping" gorm:"column:oidc_idp_username_mapping"` + OAuthAuthorizationEndpoint string `json:"authorizationEndpoint" gorm:"column:oauth_authorization_endpoint"` + OAuthTokenEndpoint string `json:"tokenEndpoint" gorm:"column:oauth_token_endpoint"` + JWTEndpoint string `json:"jwtEndpoint" gorm:"jwt_endpoint"` + JWTKeysEndpoint string `json:"keysEndpoint" gorm:"jwt_keys_endpoint"` + JWTHeaderName string `json:"headerName" gorm:"jwt_header_name"` Sequence uint64 `json:"-" gorm:"column:sequence"` InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` @@ -86,13 +83,13 @@ func IDPConfigViewToModel(idp *IDPConfigView) *model.IDPConfigView { return view } -func (i *IDPConfigView) AppendEvent(providerType model.IDPProviderType, event *models.Event) (err error) { - i.Sequence = event.Sequence - i.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { +func (i *IDPConfigView) AppendEvent(providerType model.IDPProviderType, event eventstore.Event) (err error) { + i.Sequence = event.Sequence() + i.ChangeDate = event.CreatedAt() + switch event.Type() { case instance.IDPConfigAddedEventType, org.IDPConfigAddedEventType: i.setRootData(event) - i.CreationDate = event.CreationDate + i.CreationDate = event.CreatedAt() i.IDPProviderType = int32(providerType) err = i.SetData(event) case instance.IDPOIDCConfigAddedEventType, org.IDPOIDCConfigAddedEventType: @@ -111,13 +108,14 @@ func (i *IDPConfigView) AppendEvent(providerType model.IDPProviderType, event *m return err } -func (r *IDPConfigView) setRootData(event *models.Event) { - r.AggregateID = event.AggregateID - r.InstanceID = event.InstanceID +func (r *IDPConfigView) setRootData(event eventstore.Event) { + r.AggregateID = event.Aggregate().ID + r.InstanceID = event.Aggregate().InstanceID } -func (r *IDPConfigView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { +func (r *IDPConfigView) SetData(event eventstore.Event) error { + err := event.Unmarshal(r) + if err != nil { logging.New().WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-lub6s", "Could not unmarshal data") } diff --git a/internal/iam/repository/view/model/idp_provider.go b/internal/iam/repository/view/model/idp_provider.go index 9bb0481f7..775c8ba25 100644 --- a/internal/iam/repository/view/model/idp_provider.go +++ b/internal/iam/repository/view/model/idp_provider.go @@ -1,14 +1,12 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/iam/model" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -62,26 +60,26 @@ func IDPProviderViewsToModel(providers []*IDPProviderView) []*model.IDPProviderV return result } -func (i *IDPProviderView) AppendEvent(event *models.Event) (err error) { - i.Sequence = event.Sequence - i.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { +func (i *IDPProviderView) AppendEvent(event eventstore.Event) (err error) { + i.Sequence = event.Sequence() + i.ChangeDate = event.CreatedAt() + switch event.Type() { case instance.LoginPolicyIDPProviderAddedEventType, org.LoginPolicyIDPProviderAddedEventType: i.setRootData(event) - i.CreationDate = event.CreationDate + i.CreationDate = event.CreatedAt() err = i.SetData(event) } return err } -func (r *IDPProviderView) setRootData(event *models.Event) { - r.AggregateID = event.AggregateID - r.InstanceID = event.InstanceID +func (r *IDPProviderView) setRootData(event eventstore.Event) { + r.AggregateID = event.Aggregate().ID + r.InstanceID = event.Aggregate().InstanceID } -func (r *IDPProviderView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { +func (r *IDPProviderView) SetData(event eventstore.Event) error { + if err := event.Unmarshal(r); err != nil { logging.New().WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data") } diff --git a/internal/iam/repository/view/model/label_policy.go b/internal/iam/repository/view/model/label_policy.go index e257e3399..f4fad063b 100644 --- a/internal/iam/repository/view/model/label_policy.go +++ b/internal/iam/repository/view/model/label_policy.go @@ -1,7 +1,6 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" @@ -83,15 +82,15 @@ func (p *LabelPolicyView) ToDomain() *domain.LabelPolicy { } } -func (i *LabelPolicyView) AppendEvent(event *models.Event) (err error) { +func (i *LabelPolicyView) AppendEvent(event eventstore.Event) (err error) { asset := &AssetView{} - i.Sequence = event.Sequence - i.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { + i.Sequence = event.Sequence() + i.ChangeDate = event.CreatedAt() + switch event.Type() { case instance.LabelPolicyAddedEventType, org.LabelPolicyAddedEventType: i.setRootData(event) - i.CreationDate = event.CreationDate + i.CreationDate = event.CreatedAt() i.State = int32(domain.LabelPolicyStatePreview) err = i.SetData(event) case instance.LabelPolicyChangedEventType, @@ -172,21 +171,21 @@ func (i *LabelPolicyView) AppendEvent(event *models.Event) (err error) { return err } -func (r *LabelPolicyView) setRootData(event *models.Event) { - r.AggregateID = event.AggregateID - r.InstanceID = event.InstanceID +func (r *LabelPolicyView) setRootData(event eventstore.Event) { + r.AggregateID = event.Aggregate().ID + r.InstanceID = event.Aggregate().InstanceID } -func (r *LabelPolicyView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { +func (r *LabelPolicyView) SetData(event eventstore.Event) error { + if err := event.Unmarshal(r); err != nil { logging.Log("MODEL-Flp9C").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data") } return nil } -func (r *AssetView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { +func (r *AssetView) SetData(event eventstore.Event) error { + if err := event.Unmarshal(r); err != nil { logging.Log("MODEL-Ms8f2").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data") } diff --git a/internal/iam/repository/view/model/password_complexity_policy.go b/internal/iam/repository/view/model/password_complexity_policy.go index a88603e49..30f72acb8 100644 --- a/internal/iam/repository/view/model/password_complexity_policy.go +++ b/internal/iam/repository/view/model/password_complexity_policy.go @@ -1,14 +1,12 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/iam/model" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/repository/instance" @@ -50,14 +48,14 @@ func PasswordComplexityViewToModel(policy *query.PasswordComplexityPolicy) *mode } } -func (i *PasswordComplexityPolicyView) AppendEvent(event *models.Event) (err error) { - i.Sequence = event.Sequence - i.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { +func (i *PasswordComplexityPolicyView) AppendEvent(event eventstore.Event) (err error) { + i.Sequence = event.Sequence() + i.ChangeDate = event.CreatedAt() + switch event.Type() { case instance.PasswordComplexityPolicyAddedEventType, org.PasswordComplexityPolicyAddedEventType: i.setRootData(event) - i.CreationDate = event.CreationDate + i.CreationDate = event.CreatedAt() err = i.SetData(event) case instance.PasswordComplexityPolicyChangedEventType, org.PasswordComplexityPolicyChangedEventType: @@ -66,12 +64,12 @@ func (i *PasswordComplexityPolicyView) AppendEvent(event *models.Event) (err err return err } -func (r *PasswordComplexityPolicyView) setRootData(event *models.Event) { - r.AggregateID = event.AggregateID +func (r *PasswordComplexityPolicyView) setRootData(event eventstore.Event) { + r.AggregateID = event.Aggregate().ID } -func (r *PasswordComplexityPolicyView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { +func (r *PasswordComplexityPolicyView) SetData(event eventstore.Event) error { + if err := event.Unmarshal(r); err != nil { logging.Log("EVEN-Dmi9g").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data") } diff --git a/internal/idp/providers/oauth/session.go b/internal/idp/providers/oauth/session.go index e85116afa..eb9ab49ef 100644 --- a/internal/idp/providers/oauth/session.go +++ b/internal/idp/providers/oauth/session.go @@ -56,8 +56,6 @@ func (s *Session) authorize(ctx context.Context) (err error) { return ErrCodeMissing } s.Tokens, err = rp.CodeExchange[*oidc.IDTokenClaims](ctx, s.Code, s.Provider.RelyingParty) - if err != nil { - return err - } - return nil + + return err } diff --git a/internal/integration/config/postgres.yaml b/internal/integration/config/postgres.yaml index 0ef4739e2..78866e542 100644 --- a/internal/integration/config/postgres.yaml +++ b/internal/integration/config/postgres.yaml @@ -3,7 +3,8 @@ Database: Host: localhost Port: 5432 Database: zitadel - MaxOpenConns: 20 + EventPushConnRatio: 0.2 + MaxOpenConns: 40 MaxIdleConns: 10 User: Username: zitadel diff --git a/internal/integration/config/zitadel.yaml b/internal/integration/config/zitadel.yaml index 22d77a033..5bf6937af 100644 --- a/internal/integration/config/zitadel.yaml +++ b/internal/integration/config/zitadel.yaml @@ -35,11 +35,14 @@ Quotas: Enabled: true Projections: + HandleActiveInstances: 60s Customizations: NotificationsQuotas: RequeueEvery: 1s + HandleActiveInstances: 60s Telemetry: RequeueEvery: 5s + HandleActiveInstances: 60s DefaultInstance: LoginPolicy: diff --git a/internal/migration/command.go b/internal/migration/command.go index 6552ef6ed..8a296edab 100644 --- a/internal/migration/command.go +++ b/internal/migration/command.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/api/service" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) // SetupStep is the command pushed on the eventstore @@ -35,8 +34,8 @@ func (s *SetupStep) UnmarshalJSON(data []byte) error { return nil } -func setupStartedCmd(migration Migration) eventstore.Command { - ctx := authz.SetCtxData(service.WithService(context.Background(), "system"), authz.CtxData{UserID: "system", OrgID: "SYSTEM", ResourceOwner: "SYSTEM"}) +func setupStartedCmd(ctx context.Context, migration Migration) eventstore.Command { + ctx = authz.SetCtxData(service.WithService(ctx, "system"), authz.CtxData{UserID: "system", OrgID: "SYSTEM", ResourceOwner: "SYSTEM"}) return &SetupStep{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, @@ -74,24 +73,24 @@ func setupDoneCmd(ctx context.Context, migration Migration, err error) eventstor return s } -func (s *SetupStep) Data() interface{} { +func (s *SetupStep) Payload() interface{} { return s } -func (s *SetupStep) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (s *SetupStep) UniqueConstraints() []*eventstore.UniqueConstraint { switch s.Type() { case StartedType: - return []*eventstore.EventUniqueConstraint{ - eventstore.NewAddGlobalEventUniqueConstraint("migration_started", s.migration.String(), "Errors.Step.Started.AlreadyExists"), + return []*eventstore.UniqueConstraint{ + eventstore.NewAddGlobalUniqueConstraint("migration_started", s.migration.String(), "Errors.Step.Started.AlreadyExists"), } case failedType, repeatableDoneType: - return []*eventstore.EventUniqueConstraint{ - eventstore.NewRemoveGlobalEventUniqueConstraint("migration_started", s.migration.String()), + return []*eventstore.UniqueConstraint{ + eventstore.NewRemoveGlobalUniqueConstraint("migration_started", s.migration.String()), } default: - return []*eventstore.EventUniqueConstraint{ - eventstore.NewAddGlobalEventUniqueConstraint("migration_done", s.migration.String(), "Errors.Step.Done.AlreadyExists"), + return []*eventstore.UniqueConstraint{ + eventstore.NewAddGlobalUniqueConstraint("migration_done", s.migration.String(), "Errors.Step.Done.AlreadyExists"), } } } @@ -103,14 +102,11 @@ func RegisterMappers(es *eventstore.Eventstore) { es.RegisterFilterEventMapper(aggregateType, repeatableDoneType, SetupMapper) } -func SetupMapper(event *repository.Event) (eventstore.Event, error) { +func SetupMapper(event eventstore.Event) (eventstore.Event, error) { step := &SetupStep{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - if len(event.Data) == 0 { - return step, nil - } - err := json.Unmarshal(event.Data, step) + err := event.Unmarshal(step) if err != nil { return nil, errors.ThrowInternal(err, "IAM-hYp7M", "unable to unmarshal step") } diff --git a/internal/migration/migration.go b/internal/migration/migration.go index 63a6cb7b7..d6e335516 100644 --- a/internal/migration/migration.go +++ b/internal/migration/migration.go @@ -29,6 +29,11 @@ type Migration interface { Execute(context.Context) error } +type errCheckerMigration interface { + Migration + ContinueOnErr(err error) bool +} + type RepeatableMigration interface { Migration SetLastExecution(lastRun map[string]interface{}) @@ -38,20 +43,33 @@ type RepeatableMigration interface { func Migrate(ctx context.Context, es *eventstore.Eventstore, migration Migration) (err error) { logging.WithFields("name", migration.String()).Info("verify migration") - if should, err := checkExec(ctx, es, migration); !should || err != nil { - return err + continueOnErr := func(err error) bool { + return false + } + errChecker, ok := migration.(errCheckerMigration) + if ok { + continueOnErr = errChecker.ContinueOnErr } - if _, err = es.Push(ctx, setupStartedCmd(migration)); err != nil { + // if should, err := checkExec(ctx, es, migration); !should || err != nil { + should, err := checkExec(ctx, es, migration) + if err != nil && !continueOnErr(err) { + return err + } + if !should { + return nil + } + + if _, err = es.Push(ctx, setupStartedCmd(ctx, migration)); err != nil && !continueOnErr(err) { return err } logging.WithFields("name", migration.String()).Info("starting migration") err = migration.Execute(ctx) - logging.OnError(err).Error("migration failed") + logging.WithFields("name", migration.String()).OnError(err).Error("migration failed") _, pushErr := es.Push(ctx, setupDoneCmd(ctx, migration, err)) - logging.OnError(pushErr).Error("migration failed") + logging.WithFields("name", migration.String()).OnError(pushErr).Error("migration finish failed") if err != nil { return err } @@ -127,6 +145,7 @@ func checkExec(ctx context.Context, es *eventstore.Eventstore, migration Migrati func shouldExec(ctx context.Context, es *eventstore.Eventstore, migration Migration) (should bool, err error) { events, err := es.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). OrderAsc(). + InstanceID(""). AddQuery(). AggregateTypes(aggregateType). AggregateIDs(aggregateID). diff --git a/internal/notification/handlers/already_handled.go b/internal/notification/handlers/already_handled.go index 556a3e33c..9745661ac 100644 --- a/internal/notification/handlers/already_handled.go +++ b/internal/notification/handlers/already_handled.go @@ -11,10 +11,10 @@ func (n *NotificationQueries) IsAlreadyHandled(ctx context.Context, event events ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). InstanceID(event.Aggregate().InstanceID). + SequenceGreater(event.Sequence()). AddQuery(). AggregateTypes(aggregateType). AggregateIDs(event.Aggregate().ID). - SequenceGreater(event.Sequence()). EventTypes(eventTypes...). EventData(data). Builder(), diff --git a/internal/notification/handlers/ctx.go b/internal/notification/handlers/ctx.go index eb74e558a..9dc6d87b1 100644 --- a/internal/notification/handlers/ctx.go +++ b/internal/notification/handlers/ctx.go @@ -9,7 +9,7 @@ import ( const NotifyUserID = "NOTIFICATION" //TODO: system? -func HandlerContext(event eventstore.Aggregate) context.Context { +func HandlerContext(event *eventstore.Aggregate) context.Context { ctx := authz.WithInstanceID(context.Background(), event.InstanceID) return authz.SetCtxData(ctx, authz.CtxData{UserID: NotifyUserID, OrgID: event.ResourceOwner}) } diff --git a/internal/notification/handlers/quota_notifier.go b/internal/notification/handlers/quota_notifier.go index b8f59a70e..b149643ea 100644 --- a/internal/notification/handlers/quota_notifier.go +++ b/internal/notification/handlers/quota_notifier.go @@ -7,12 +7,10 @@ import ( "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/notification/channels/webhook" _ "github.com/zitadel/zitadel/internal/notification/statik" "github.com/zitadel/zitadel/internal/notification/types" - "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/quota" ) @@ -21,7 +19,6 @@ const ( ) type quotaNotifier struct { - crdb.StatementHandler commands *command.Commands queries *NotificationQueries channels types.ChannelChains @@ -29,27 +26,28 @@ type quotaNotifier struct { func NewQuotaNotifier( ctx context.Context, - config crdb.StatementHandlerConfig, + config handler.Config, commands *command.Commands, queries *NotificationQueries, channels types.ChannelChains, -) *quotaNotifier { - p := new(quotaNotifier) - config.ProjectionName = QuotaNotificationsProjectionTable - config.Reducers = p.reducers() - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - p.commands = commands - p.queries = queries - p.channels = channels - projection.NotificationsQuotaProjection = p - return p +) *handler.Handler { + return handler.NewHandler(ctx, &config, "aNotifier{ + commands: commands, + queries: queries, + channels: channels, + }) + } -func (u *quotaNotifier) reducers() []handler.AggregateReducer { +func (*quotaNotifier) Name() string { + return QuotaNotificationsProjectionTable +} + +func (u *quotaNotifier) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: quota.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: quota.NotificationDueEventType, Reduce: u.reduceNotificationDue, @@ -64,21 +62,20 @@ func (u *quotaNotifier) reduceNotificationDue(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DLxdE", "reduce.wrong.event.type %s", quota.NotificationDueEventType) } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, map[string]interface{}{"dueEventID": e.ID}, quota.AggregateType, quota.NotifiedEventType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - err = types.SendJSON(ctx, webhook.Config{CallURL: e.CallURL, Method: http.MethodPost}, u.channels, e, e).WithoutTemplate() - if err != nil { - return nil, err - } - err = u.commands.UsageNotificationSent(ctx, e) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, map[string]interface{}{"dueEventID": e.ID}, quota.AggregateType, quota.NotifiedEventType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + err = types.SendJSON(ctx, webhook.Config{CallURL: e.CallURL, Method: http.MethodPost}, u.channels, e, e).WithoutTemplate() + if err != nil { + return err + } + return u.commands.UsageNotificationSent(ctx, e) + }), nil } diff --git a/internal/notification/handlers/telemetry_pusher.go b/internal/notification/handlers/telemetry_pusher.go index 5b802a050..b326bbc96 100644 --- a/internal/notification/handlers/telemetry_pusher.go +++ b/internal/notification/handlers/telemetry_pusher.go @@ -3,7 +3,6 @@ package handlers import ( "context" "fmt" - "math" "net/http" "time" @@ -14,13 +13,11 @@ import ( "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/notification/channels/webhook" _ "github.com/zitadel/zitadel/internal/notification/statik" "github.com/zitadel/zitadel/internal/notification/types" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/milestone" "github.com/zitadel/zitadel/internal/repository/pseudo" ) @@ -37,7 +34,6 @@ type TelemetryPusherConfig struct { } type telemetryPusher struct { - crdb.StatementHandler cfg TelemetryPusherConfig commands *command.Commands queries *NotificationQueries @@ -47,28 +43,33 @@ type telemetryPusher struct { func NewTelemetryPusher( ctx context.Context, telemetryCfg TelemetryPusherConfig, - handlerCfg crdb.StatementHandlerConfig, + handlerCfg handler.Config, commands *command.Commands, queries *NotificationQueries, channels types.ChannelChains, -) *telemetryPusher { - p := new(telemetryPusher) - handlerCfg.ProjectionName = TelemetryProjectionTable - handlerCfg.Reducers = p.reducers() - p.cfg = telemetryCfg - handlerCfg.ConcurrentInstances = math.MaxInt - p.StatementHandler = crdb.NewStatementHandler(ctx, handlerCfg) - p.commands = commands - p.queries = queries - p.channels = channels - projection.TelemetryPusherProjection = p - return p +) *handler.Handler { + pusher := &telemetryPusher{ + cfg: telemetryCfg, + commands: commands, + queries: queries, + channels: channels, + } + handlerCfg.TriggerWithoutEvents = pusher.pushMilestones + return handler.NewHandler( + ctx, + &handlerCfg, + pusher, + ) } -func (t *telemetryPusher) reducers() []handler.AggregateReducer { +func (u *telemetryPusher) Name() string { + return TelemetryProjectionTable +} + +func (t *telemetryPusher) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{{ Aggregate: pseudo.AggregateType, - EventRedusers: []handler.EventReducer{{ + EventReducers: []handler.EventReducer{{ Event: pseudo.ScheduledEventType, Reduce: t.pushMilestones, }}, @@ -82,41 +83,42 @@ func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.State return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-lDTs5", "reduce.wrong.event.type %s", event.Type()) } - isReached, err := query.NewNotNullQuery(query.MilestoneReachedDateColID) - if err != nil { - return nil, err - } - isNotPushed, err := query.NewIsNullQuery(query.MilestonePushedDateColID) - if err != nil { - return nil, err - } - hasPrimaryDomain, err := query.NewNotNullQuery(query.MilestonePrimaryDomainColID) - if err != nil { - return nil, err - } - unpushedMilestones, err := t.queries.Queries.SearchMilestones(ctx, scheduledEvent.InstanceIDs, &query.MilestonesSearchQueries{ - SearchRequest: query.SearchRequest{ - Limit: t.cfg.Limit, - SortingColumn: query.MilestoneReachedDateColID, - Asc: true, - }, - Queries: []query.SearchQuery{isReached, isNotPushed, hasPrimaryDomain}, - }) - if err != nil { - return nil, err - } - var errs int - for _, ms := range unpushedMilestones.Milestones { - if err = t.pushMilestone(ctx, scheduledEvent, ms); err != nil { - errs++ - logging.Warnf("pushing milestone %+v failed: %s", *ms, err.Error()) + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + isReached, err := query.NewNotNullQuery(query.MilestoneReachedDateColID) + if err != nil { + return err } - } - if errs > 0 { - return nil, fmt.Errorf("pushing %d of %d milestones failed", errs, unpushedMilestones.Count) - } - - return crdb.NewNoOpStatement(scheduledEvent), nil + isNotPushed, err := query.NewIsNullQuery(query.MilestonePushedDateColID) + if err != nil { + return err + } + hasPrimaryDomain, err := query.NewNotNullQuery(query.MilestonePrimaryDomainColID) + if err != nil { + return err + } + unpushedMilestones, err := t.queries.Queries.SearchMilestones(ctx, scheduledEvent.InstanceIDs, &query.MilestonesSearchQueries{ + SearchRequest: query.SearchRequest{ + Limit: t.cfg.Limit, + SortingColumn: query.MilestoneReachedDateColID, + Asc: true, + }, + Queries: []query.SearchQuery{isReached, isNotPushed, hasPrimaryDomain}, + }) + if err != nil { + return err + } + var errs int + for _, ms := range unpushedMilestones.Milestones { + if err = t.pushMilestone(ctx, scheduledEvent, ms); err != nil { + errs++ + logging.Warnf("pushing milestone %+v failed: %s", *ms, err.Error()) + } + } + if errs > 0 { + return fmt.Errorf("pushing %d of %d milestones failed", errs, unpushedMilestones.Count) + } + return nil + }), nil } func (t *telemetryPusher) pushMilestone(ctx context.Context, event *pseudo.ScheduledEvent, ms *query.Milestone) error { diff --git a/internal/notification/handlers/user_notifier.go b/internal/notification/handlers/user_notifier.go index 1c60fea8c..c54cae11d 100644 --- a/internal/notification/handlers/user_notifier.go +++ b/internal/notification/handlers/user_notifier.go @@ -11,11 +11,9 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/notification/types" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/session" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -25,7 +23,6 @@ const ( ) type userNotifier struct { - crdb.StatementHandler commands Commands queries *NotificationQueries channels types.ChannelChains @@ -34,29 +31,29 @@ type userNotifier struct { func NewUserNotifier( ctx context.Context, - config crdb.StatementHandlerConfig, + config handler.Config, commands Commands, queries *NotificationQueries, channels types.ChannelChains, otpEmailTmpl string, -) *userNotifier { - p := new(userNotifier) - config.ProjectionName = UserNotificationsProjectionTable - config.Reducers = p.reducers() - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - p.commands = commands - p.queries = queries - p.channels = channels - p.otpEmailTmpl = otpEmailTmpl - projection.NotificationsProjection = p - return p +) *handler.Handler { + return handler.NewHandler(ctx, &config, &userNotifier{ + commands: commands, + queries: queries, + otpEmailTmpl: otpEmailTmpl, + channels: channels, + }) } -func (u *userNotifier) reducers() []handler.AggregateReducer { +func (u *userNotifier) Name() string { + return UserNotificationsProjectionTable +} + +func (u *userNotifier) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserV1InitialCodeAddedType, Reduce: u.reduceInitCodeAdded, @@ -113,7 +110,7 @@ func (u *userNotifier) reducers() []handler.AggregateReducer { }, { Aggregate: session.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: session.OTPSMSChallengedType, Reduce: u.reduceSessionOTPSMSChallenged, @@ -132,52 +129,52 @@ func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Sta if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-EFe2f", "reduce.wrong.event.type %s", user.HumanInitialCodeAddedType) } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, - user.UserV1InitialCodeAddedType, user.UserV1InitialCodeSentType, - user.HumanInitialCodeAddedType, user.HumanInitialCodeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) - if err != nil { - return nil, err - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } - template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, + user.UserV1InitialCodeAddedType, user.UserV1InitialCodeSentType, + user.HumanInitialCodeAddedType, user.HumanInitialCodeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) + if err != nil { + return err + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.InitCodeMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendUserInitCode(ctx, notifyUser, code) - if err != nil { - return nil, err - } - err = u.commands.HumanInitCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } + + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.InitCodeMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). + SendUserInitCode(ctx, notifyUser, code) + if err != nil { + return err + } + return u.commands.HumanInitCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.Statement, error) { @@ -187,54 +184,54 @@ func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.St } if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil - } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, - user.UserV1EmailCodeAddedType, user.UserV1EmailCodeSentType, - user.HumanEmailCodeAddedType, user.HumanEmailCodeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) - if err != nil { - return nil, err - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err + return handler.NewNoOpStatement(e), nil } - template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, + user.UserV1EmailCodeAddedType, user.UserV1EmailCodeSentType, + user.HumanEmailCodeAddedType, user.HumanEmailCodeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) + if err != nil { + return err + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.VerifyEmailMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate) - if err != nil { - return nil, err - } - err = u.commands.HumanEmailVerificationCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } + + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.VerifyEmailMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). + SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate) + if err != nil { + return err + } + return u.commands.HumanEmailVerificationCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler.Statement, error) { @@ -243,57 +240,57 @@ func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Eeg3s", "reduce.wrong.event.type %s", user.HumanPasswordCodeAddedType) } if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil - } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, - user.UserV1PasswordCodeAddedType, user.UserV1PasswordCodeSentType, - user.HumanPasswordCodeAddedType, user.HumanPasswordCodeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) - if err != nil { - return nil, err - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err + return handler.NewNoOpStatement(e), nil } - template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, + user.UserV1PasswordCodeAddedType, user.UserV1PasswordCodeSentType, + user.HumanPasswordCodeAddedType, user.HumanPasswordCodeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) + if err != nil { + return err + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordResetMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - notify := types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e) - if e.NotificationType == domain.NotificationTypeSms { - notify = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e) - } - err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate) - if err != nil { - return nil, err - } - err = u.commands.PasswordCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } + + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordResetMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + notify := types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e) + if e.NotificationType == domain.NotificationTypeSms { + notify = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e) + } + err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate) + if err != nil { + return err + } + return u.commands.PasswordCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) reduceOTPSMSCodeAdded(event eventstore.Event) (*handler.Statement, error) { @@ -319,7 +316,7 @@ func (u *userNotifier) reduceSessionOTPSMSChallenged(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sk32L", "reduce.wrong.event.type %s", session.OTPSMSChallengedType) } if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } ctx := HandlerContext(event.Aggregate()) s, err := u.queries.SessionByID(ctx, true, e.Aggregate().ID, "") @@ -353,7 +350,7 @@ func (u *userNotifier) reduceOTPSMS( return nil, err } if alreadyHandled { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } plainCode, err := crypto.DecryptString(code, u.queries.UserDataCrypto) if err != nil { @@ -385,7 +382,7 @@ func (u *userNotifier) reduceOTPSMS( if err != nil { return nil, err } - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } func (u *userNotifier) reduceOTPEmailCodeAdded(event eventstore.Event) (*handler.Statement, error) { @@ -419,7 +416,7 @@ func (u *userNotifier) reduceSessionOTPEmailChallenged(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-zbsgt", "reduce.wrong.event.type %s", session.OTPEmailChallengedType) } if e.ReturnCode { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } ctx := HandlerContext(event.Aggregate()) s, err := u.queries.SessionByID(ctx, true, e.Aggregate().ID, "") @@ -466,7 +463,7 @@ func (u *userNotifier) reduceOTPEmail( return nil, err } if alreadyHandled { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } plainCode, err := crypto.DecryptString(code, u.queries.UserDataCrypto) if err != nil { @@ -507,7 +504,7 @@ func (u *userNotifier) reduceOTPEmail( if err != nil { return nil, err } - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } func (u *userNotifier) reduceDomainClaimed(event eventstore.Event) (*handler.Statement, error) { @@ -515,47 +512,46 @@ func (u *userNotifier) reduceDomainClaimed(event eventstore.Event) (*handler.Sta if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Drh5w", "reduce.wrong.event.type %s", user.UserDomainClaimedType) } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType, - user.UserDomainClaimedType, user.UserDomainClaimedSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType, + user.UserDomainClaimedType, user.UserDomainClaimedSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.DomainClaimedMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendDomainClaimed(ctx, notifyUser, e.UserName) - if err != nil { - return nil, err - } - err = u.commands.UserDomainClaimedSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.DomainClaimedMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). + SendDomainClaimed(ctx, notifyUser, e.UserName) + if err != nil { + return err + } + return u.commands.UserDomainClaimedSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) (*handler.Statement, error) { @@ -564,52 +560,52 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-EDtjd", "reduce.wrong.event.type %s", user.HumanPasswordlessInitCodeAddedType) } if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil - } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) - if err != nil { - return nil, err - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err + return handler.NewNoOpStatement(e), nil } - template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) + if err != nil { + return err + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordlessRegistrationMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendPasswordlessRegistrationLink(ctx, notifyUser, code, e.ID, e.URLTemplate) - if err != nil { - return nil, err - } - err = u.commands.HumanPasswordlessInitCodeSent(ctx, e.Aggregate().ID, e.Aggregate().ResourceOwner, e.ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } + + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordlessRegistrationMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). + SendPasswordlessRegistrationLink(ctx, notifyUser, code, e.ID, e.URLTemplate) + if err != nil { + return err + } + return u.commands.HumanPasswordlessInitCodeSent(ctx, e.Aggregate().ID, e.Aggregate().ResourceOwner, e.ID) + }), nil } func (u *userNotifier) reducePasswordChanged(event eventstore.Event) (*handler.Statement, error) { @@ -617,57 +613,58 @@ func (u *userNotifier) reducePasswordChanged(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType) } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType, user.HumanPasswordChangeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - notificationPolicy, err := u.queries.NotificationPolicyByOrg(ctx, true, e.Aggregate().ResourceOwner, false) - if errors.IsNotFound(err) { - return crdb.NewNoOpStatement(e), nil - } - if err != nil { - return nil, err - } + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.AggregateType, user.HumanPasswordChangeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + + notificationPolicy, err := u.queries.NotificationPolicyByOrg(ctx, true, e.Aggregate().ResourceOwner, false) + if errors.IsNotFound(err) { + return nil + } + if err != nil { + return err + } + + if !notificationPolicy.PasswordChange { + return nil + } - if notificationPolicy.PasswordChange { colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) if err != nil { - return nil, err + return err } template, err := u.queries.MailTemplateByOrg(ctx, e.Aggregate().ResourceOwner, false) if err != nil { - return nil, err + return err } notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) if err != nil { - return nil, err + return err } translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.PasswordChangeMessageType) if err != nil { - return nil, err + return err } ctx, err = u.queries.Origin(ctx, e) if err != nil { - return nil, err + return err } err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). SendPasswordChange(ctx, notifyUser) if err != nil { - return nil, err + return err } - err = u.commands.PasswordChangeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - } - return crdb.NewNoOpStatement(e), nil + return u.commands.PasswordChangeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) reducePhoneCodeAdded(event eventstore.Event) (*handler.Statement, error) { @@ -676,53 +673,53 @@ func (u *userNotifier) reducePhoneCodeAdded(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-He83g", "reduce.wrong.event.type %s", user.HumanPhoneCodeAddedType) } if e.CodeReturned { - return crdb.NewNoOpStatement(e), nil - } - ctx := HandlerContext(event.Aggregate()) - alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, - user.UserV1PhoneCodeAddedType, user.UserV1PhoneCodeSentType, - user.HumanPhoneCodeAddedType, user.HumanPhoneCodeSentType) - if err != nil { - return nil, err - } - if alreadyHandled { - return crdb.NewNoOpStatement(e), nil - } - code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) - if err != nil { - return nil, err - } - colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) - if err != nil { - return nil, err + return handler.NewNoOpStatement(e), nil } - notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) - if err != nil { - return nil, err - } - translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.VerifyPhoneMessageType) - if err != nil { - return nil, err - } - ctx, err = u.queries.Origin(ctx, e) - if err != nil { - return nil, err - } - err = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e). - SendPhoneVerificationCode(ctx, code) - if err != nil { - return nil, err - } - err = u.commands.HumanPhoneVerificationCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) - if err != nil { - return nil, err - } - return crdb.NewNoOpStatement(e), nil + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + ctx := HandlerContext(event.Aggregate()) + alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil, + user.UserV1PhoneCodeAddedType, user.UserV1PhoneCodeSentType, + user.HumanPhoneCodeAddedType, user.HumanPhoneCodeSentType) + if err != nil { + return err + } + if alreadyHandled { + return nil + } + code, err := crypto.DecryptString(e.Code, u.queries.UserDataCrypto) + if err != nil { + return err + } + colors, err := u.queries.ActiveLabelPolicyByOrg(ctx, e.Aggregate().ResourceOwner, false) + if err != nil { + return err + } + + notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false) + if err != nil { + return err + } + translator, err := u.queries.GetTranslatorWithOrgTexts(ctx, notifyUser.ResourceOwner, domain.VerifyPhoneMessageType) + if err != nil { + return err + } + + ctx, err = u.queries.Origin(ctx, e) + if err != nil { + return err + } + err = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e). + SendPhoneVerificationCode(ctx, code) + if err != nil { + return err + } + return u.commands.HumanPhoneVerificationCodeSent(ctx, e.Aggregate().ResourceOwner, e.Aggregate().ID) + }), nil } func (u *userNotifier) checkIfCodeAlreadyHandledOrExpired(ctx context.Context, event eventstore.Event, expiry time.Duration, data map[string]interface{}, eventTypes ...eventstore.EventType) (bool, error) { - if event.CreationDate().Add(expiry).Before(time.Now().UTC()) { + if event.CreatedAt().Add(expiry).Before(time.Now().UTC()) { return true, nil } return u.queries.IsAlreadyHandled(ctx, event, data, user.AggregateType, eventTypes...) diff --git a/internal/notification/handlers/user_notifier_test.go b/internal/notification/handlers/user_notifier_test.go index db3569ad2..797aa7b87 100644 --- a/internal/notification/handlers/user_notifier_test.go +++ b/internal/notification/handlers/user_notifier_test.go @@ -72,9 +72,9 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanInitialCodeAddedEvent{ @@ -111,9 +111,9 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanInitialCodeAddedEvent{ @@ -144,9 +144,9 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanInitialCodeAddedEvent{ @@ -184,9 +184,9 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanInitialCodeAddedEvent{ @@ -210,7 +210,13 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceInitCodeAdded(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceInitCodeAdded(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -241,9 +247,9 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanEmailCodeAddedEvent{ @@ -282,9 +288,9 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanEmailCodeAddedEvent{ @@ -317,9 +323,9 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -360,9 +366,9 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanEmailCodeAddedEvent{ @@ -396,9 +402,9 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -425,7 +431,13 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceEmailCodeAdded(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceEmailCodeAdded(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -456,9 +468,9 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordCodeAddedEvent{ @@ -497,9 +509,9 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordCodeAddedEvent{ @@ -532,9 +544,9 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -575,9 +587,9 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordCodeAddedEvent{ @@ -611,9 +623,9 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -640,7 +652,13 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordCodeAdded(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordCodeAdded(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -670,9 +688,9 @@ func Test_userNotifier_reduceDomainClaimed(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), }, args{ event: &user.DomainClaimedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ @@ -705,9 +723,9 @@ func Test_userNotifier_reduceDomainClaimed(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), }, args{ event: &user.DomainClaimedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ @@ -727,7 +745,13 @@ func Test_userNotifier_reduceDomainClaimed(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceDomainClaimed(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceDomainClaimed(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -758,9 +782,9 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordlessInitCodeRequestedEvent{ @@ -800,9 +824,9 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordlessInitCodeRequestedEvent{ @@ -836,9 +860,9 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -880,9 +904,9 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &user.HumanPasswordlessInitCodeRequestedEvent{ @@ -917,9 +941,9 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -947,7 +971,13 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordlessCodeRequested(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordlessCodeRequested(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -980,9 +1010,9 @@ func Test_userNotifier_reducePasswordChanged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), }, args{ event: &user.HumanPasswordChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ @@ -1018,9 +1048,9 @@ func Test_userNotifier_reducePasswordChanged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), }, args{ event: &user.HumanPasswordChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ @@ -1040,7 +1070,13 @@ func Test_userNotifier_reducePasswordChanged(t *testing.T) { queries := mock.NewMockQueries(ctrl) commands := mock.NewMockCommands(ctrl) f, a, w := tt.test(ctrl, queries, commands) - _, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordChanged(a.event) + stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordChanged(a.event) + if w.err != nil { + w.err(t, err) + } else { + assert.NoError(t, err) + } + err = stmt.Execute(nil, "") if w.err != nil { w.err(t, err) } else { @@ -1072,9 +1108,9 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &session.OTPEmailChallengedEvent{ @@ -1114,9 +1150,9 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &session.OTPEmailChallengedEvent{ @@ -1150,9 +1186,9 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ @@ -1194,9 +1230,9 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, }, args{ event: &session.OTPEmailChallengedEvent{ @@ -1230,9 +1266,9 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) { return fields{ queries: queries, commands: commands, - es: eventstore.NewEventstore(eventstore.TestConfig( - es_repo_mock.NewRepo(t).ExpectFilterEvents(), - )), + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), userDataCrypto: codeAlg, SMSTokenCrypto: nil, }, args{ diff --git a/internal/notification/projections.go b/internal/notification/projections.go index f647a8c41..b2630d233 100644 --- a/internal/notification/projections.go +++ b/internal/notification/projections.go @@ -33,9 +33,9 @@ func Start( logging.OnError(err).Panic("unable to start listener") q := handlers.NewNotificationQueries(queries, es, externalDomain, externalPort, externalSecure, fileSystemPath, userEncryption, smtpEncryption, smsEncryption, statikFS) c := newChannels(q) - handlers.NewUserNotifier(ctx, projection.ApplyCustomConfig(userHandlerCustomConfig), commands, q, c, otpEmailTmpl).Start() - handlers.NewQuotaNotifier(ctx, projection.ApplyCustomConfig(quotaHandlerCustomConfig), commands, q, c).Start() + handlers.NewUserNotifier(ctx, projection.ApplyCustomConfig(userHandlerCustomConfig), commands, q, c, otpEmailTmpl).Start(ctx) + handlers.NewQuotaNotifier(ctx, projection.ApplyCustomConfig(quotaHandlerCustomConfig), commands, q, c).Start(ctx) if telemetryCfg.Enabled { - handlers.NewTelemetryPusher(ctx, telemetryCfg, projection.ApplyCustomConfig(telemetryHandlerCustomConfig), commands, q, c).Start() + handlers.NewTelemetryPusher(ctx, telemetryCfg, projection.ApplyCustomConfig(telemetryHandlerCustomConfig), commands, q, c).Start(ctx) } } diff --git a/internal/notification/senders/email.go b/internal/notification/senders/email.go index ea93c0911..5123798f4 100644 --- a/internal/notification/senders/email.go +++ b/internal/notification/senders/email.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/notification/channels" "github.com/zitadel/zitadel/internal/notification/channels/fs" diff --git a/internal/notification/senders/webhook.go b/internal/notification/senders/webhook.go index d677f9a80..050168432 100644 --- a/internal/notification/senders/webhook.go +++ b/internal/notification/senders/webhook.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/notification/channels" "github.com/zitadel/zitadel/internal/notification/channels/fs" diff --git a/internal/org/repository/eventsourcing/model/domain.go b/internal/org/repository/eventsourcing/model/domain.go index 2f1fe880d..902805772 100644 --- a/internal/org/repository/eventsourcing/model/domain.go +++ b/internal/org/repository/eventsourcing/model/domain.go @@ -1,10 +1,9 @@ package model import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/org/model" ) @@ -28,18 +27,18 @@ func GetDomain(domains []*OrgDomain, domain string) (int, *OrgDomain) { return -1, nil } -func (o *Org) appendAddDomainEvent(event *es_models.Event) error { +func (o *Org) appendAddDomainEvent(event eventstore.Event) error { domain := new(OrgDomain) err := domain.SetData(event) if err != nil { return err } - domain.ObjectRoot.CreationDate = event.CreationDate + domain.ObjectRoot.CreationDate = event.CreatedAt() o.Domains = append(o.Domains, domain) return nil } -func (o *Org) appendRemoveDomainEvent(event *es_models.Event) error { +func (o *Org) appendRemoveDomainEvent(event eventstore.Event) error { domain := new(OrgDomain) err := domain.SetData(event) if err != nil { @@ -53,7 +52,7 @@ func (o *Org) appendRemoveDomainEvent(event *es_models.Event) error { return nil } -func (o *Org) appendVerifyDomainEvent(event *es_models.Event) error { +func (o *Org) appendVerifyDomainEvent(event eventstore.Event) error { domain := new(OrgDomain) err := domain.SetData(event) if err != nil { @@ -66,7 +65,7 @@ func (o *Org) appendVerifyDomainEvent(event *es_models.Event) error { return nil } -func (o *Org) appendPrimaryDomainEvent(event *es_models.Event) error { +func (o *Org) appendPrimaryDomainEvent(event eventstore.Event) error { domain := new(OrgDomain) err := domain.SetData(event) if err != nil { @@ -81,7 +80,7 @@ func (o *Org) appendPrimaryDomainEvent(event *es_models.Event) error { return nil } -func (o *Org) appendVerificationDomainEvent(event *es_models.Event) error { +func (o *Org) appendVerificationDomainEvent(event eventstore.Event) error { domain := new(OrgDomain) err := domain.SetData(event) if err != nil { @@ -96,8 +95,8 @@ func (o *Org) appendVerificationDomainEvent(event *es_models.Event) error { return nil } -func (m *OrgDomain) SetData(event *es_models.Event) error { - err := json.Unmarshal(event.Data, m) +func (m *OrgDomain) SetData(event eventstore.Event) error { + err := event.Unmarshal(m) if err != nil { return errors.ThrowInternal(err, "EVENT-Hz7Mb", "unable to unmarshal data") } diff --git a/internal/org/repository/eventsourcing/model/domain_policy.go b/internal/org/repository/eventsourcing/model/domain_policy.go index 445198955..8b504f43c 100644 --- a/internal/org/repository/eventsourcing/model/domain_policy.go +++ b/internal/org/repository/eventsourcing/model/domain_policy.go @@ -1,21 +1,21 @@ package model import ( - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" iam_es_model "github.com/zitadel/zitadel/internal/iam/repository/eventsourcing/model" ) -func (o *Org) appendAddDomainPolicyEvent(event *es_models.Event) error { +func (o *Org) appendAddDomainPolicyEvent(event eventstore.Event) error { o.DomainPolicy = new(iam_es_model.DomainPolicy) err := o.DomainPolicy.SetData(event) if err != nil { return err } - o.DomainPolicy.ObjectRoot.CreationDate = event.CreationDate + o.DomainPolicy.ObjectRoot.CreationDate = event.CreatedAt() return nil } -func (o *Org) appendChangeDomainPolicyEvent(event *es_models.Event) error { +func (o *Org) appendChangeDomainPolicyEvent(event eventstore.Event) error { return o.DomainPolicy.SetData(event) } diff --git a/internal/org/repository/eventsourcing/model/org.go b/internal/org/repository/eventsourcing/model/org.go index f3ebacd85..5de94e499 100644 --- a/internal/org/repository/eventsourcing/model/org.go +++ b/internal/org/repository/eventsourcing/model/org.go @@ -1,8 +1,6 @@ package model import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" @@ -34,7 +32,7 @@ func OrgToModel(org *Org) *org_model.Org { return converted } -func OrgFromEvents(org *Org, events ...*es_models.Event) (*Org, error) { +func OrgFromEvents(org *Org, events ...eventstore.Event) (*Org, error) { if org == nil { org = new(Org) } @@ -42,7 +40,7 @@ func OrgFromEvents(org *Org, events ...*es_models.Event) (*Org, error) { return org, org.AppendEvents(events...) } -func (o *Org) AppendEvents(events ...*es_models.Event) error { +func (o *Org) AppendEvents(events ...eventstore.Event) error { for _, event := range events { err := o.AppendEvent(event) if err != nil { @@ -52,8 +50,8 @@ func (o *Org) AppendEvents(events ...*es_models.Event) error { return nil } -func (o *Org) AppendEvent(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { +func (o *Org) AppendEvent(event eventstore.Event) (err error) { + switch event.Type() { case org.OrgAddedEventType: err = o.SetData(event) if err != nil { @@ -92,8 +90,8 @@ func (o *Org) AppendEvent(event *es_models.Event) (err error) { return nil } -func (o *Org) SetData(event *es_models.Event) error { - err := json.Unmarshal(event.Data, o) +func (o *Org) SetData(event eventstore.Event) error { + err := event.Unmarshal(o) if err != nil { return errors.ThrowInternal(err, "EVENT-BpbQZ", "unable to unmarshal event") } diff --git a/internal/org/repository/eventsourcing/model/org_test.go b/internal/org/repository/eventsourcing/model/org_test.go index 6af1d8af0..af6cd9633 100644 --- a/internal/org/repository/eventsourcing/model/org_test.go +++ b/internal/org/repository/eventsourcing/model/org_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "testing" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/org/model" "github.com/zitadel/zitadel/internal/repository/org" @@ -11,7 +12,7 @@ import ( func TestOrgFromEvents(t *testing.T) { type args struct { - event []*es_models.Event + event []eventstore.Event org *Org } tests := []struct { @@ -22,8 +23,8 @@ func TestOrgFromEvents(t *testing.T) { { name: "org from events, ok", args: args{ - event: []*es_models.Event{ - {AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgAddedEventType)}, + event: []eventstore.Event{ + &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgAddedEventType}, }, org: &Org{Name: "OrgName"}, }, @@ -32,8 +33,8 @@ func TestOrgFromEvents(t *testing.T) { { name: "org from events, nil org", args: args{ - event: []*es_models.Event{ - {AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgAddedEventType)}, + event: []eventstore.Event{ + &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgAddedEventType}, }, org: nil, }, @@ -44,7 +45,7 @@ func TestOrgFromEvents(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.args.org != nil { data, _ := json.Marshal(tt.args.org) - tt.args.event[0].Data = data + tt.args.event[0].(*es_models.Event).Data = data } result, _ := OrgFromEvents(tt.args.org, tt.args.event...) if result.Name != tt.result.Name { @@ -67,7 +68,7 @@ func TestAppendEvent(t *testing.T) { { name: "append added event", args: args{ - event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgAddedEventType)}, + event: &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgAddedEventType}, org: &Org{Name: "OrgName"}, }, result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.OrgStateActive), Name: "OrgName"}, @@ -75,7 +76,7 @@ func TestAppendEvent(t *testing.T) { { name: "append change event", args: args{ - event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgChangedEventType), Data: []byte(`{"name": "OrgName}`)}, + event: &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgChangedEventType, Data: []byte(`{"name": "OrgName}`)}, org: &Org{Name: "OrgNameChanged"}, }, result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.OrgStateActive), Name: "OrgNameChanged"}, @@ -83,14 +84,14 @@ func TestAppendEvent(t *testing.T) { { name: "append deactivate event", args: args{ - event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgDeactivatedEventType)}, + event: &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgDeactivatedEventType}, }, result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.OrgStateInactive)}, }, { name: "append reactivate event", args: args{ - event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: es_models.EventType(org.OrgReactivatedEventType)}, + event: &es_models.Event{AggregateID: "ID", Seq: 1, Typ: org.OrgReactivatedEventType}, }, result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.OrgStateActive)}, }, diff --git a/internal/org/repository/view/query.go b/internal/org/repository/view/query.go index 765d6abbc..21b39a223 100644 --- a/internal/org/repository/view/query.go +++ b/internal/org/repository/view/query.go @@ -2,33 +2,34 @@ package view import ( "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/org" ) -func OrgByIDQuery(id, instanceID string, latestSequence uint64) (*es_models.SearchQuery, error) { +func OrgByIDQuery(id, instanceID string, latestSequence uint64) (*eventstore.SearchQueryBuilder, error) { if id == "" { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dke74", "id should be filled") } - return es_models.NewSearchQuery(). + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + InstanceID(instanceID). + AwaitOpenTransactions(). + SequenceGreater(latestSequence). AddQuery(). - AggregateTypeFilter(org.AggregateType). - LatestSequenceFilter(latestSequence). - InstanceIDFilter(instanceID). - AggregateIDFilter(id). - EventTypesFilter( - es_models.EventType(org.OrgAddedEventType), - es_models.EventType(org.OrgChangedEventType), - es_models.EventType(org.OrgDeactivatedEventType), - es_models.EventType(org.OrgReactivatedEventType), - es_models.EventType(org.OrgDomainAddedEventType), - es_models.EventType(org.OrgDomainVerificationAddedEventType), - es_models.EventType(org.OrgDomainVerifiedEventType), - es_models.EventType(org.OrgDomainPrimarySetEventType), - es_models.EventType(org.OrgDomainRemovedEventType), - es_models.EventType(org.DomainPolicyAddedEventType), - es_models.EventType(org.DomainPolicyChangedEventType), - es_models.EventType(org.DomainPolicyRemovedEventType), + AggregateTypes(org.AggregateType). + AggregateIDs(id). + EventTypes( + org.OrgAddedEventType, + org.OrgChangedEventType, + org.OrgDeactivatedEventType, + org.OrgReactivatedEventType, + org.OrgDomainAddedEventType, + org.OrgDomainVerificationAddedEventType, + org.OrgDomainVerifiedEventType, + org.OrgDomainPrimarySetEventType, + org.OrgDomainRemovedEventType, + org.DomainPolicyAddedEventType, + org.DomainPolicyChangedEventType, + org.DomainPolicyRemovedEventType, ). - SearchQuery(), nil + Builder(), nil } diff --git a/internal/project/repository/eventsourcing/model/oidc_config.go b/internal/project/repository/eventsourcing/model/oidc_config.go index f051defc5..fb75374aa 100644 --- a/internal/project/repository/eventsourcing/model/oidc_config.go +++ b/internal/project/repository/eventsourcing/model/oidc_config.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/project" ) @@ -75,7 +74,7 @@ func (key *ClientKey) AppendEvents(events ...*es_models.Event) error { func (key *ClientKey) AppendEvent(event *es_models.Event) (err error) { key.ObjectRoot.AppendEvent(event) - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.ApplicationKeyAddedEventType: err = json.Unmarshal(event.Data, key) if err != nil { diff --git a/internal/project/repository/eventsourcing/model/project.go b/internal/project/repository/eventsourcing/model/project.go index dfba580f7..032850098 100644 --- a/internal/project/repository/eventsourcing/model/project.go +++ b/internal/project/repository/eventsourcing/model/project.go @@ -1,8 +1,6 @@ package model import ( - "encoding/json" - "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/eventstore" @@ -41,7 +39,7 @@ func ProjectToModel(project *Project) *model.Project { } } -func ProjectFromEvents(project *Project, events ...*es_models.Event) (*Project, error) { +func ProjectFromEvents(project *Project, events ...eventstore.Event) (*Project, error) { if project == nil { project = &Project{} } @@ -49,7 +47,7 @@ func ProjectFromEvents(project *Project, events ...*es_models.Event) (*Project, return project, project.AppendEvents(events...) } -func (p *Project) AppendEvents(events ...*es_models.Event) error { +func (p *Project) AppendEvents(events ...eventstore.Event) error { for _, event := range events { if err := p.AppendEvent(event); err != nil { return err @@ -58,10 +56,10 @@ func (p *Project) AppendEvents(events ...*es_models.Event) error { return nil } -func (p *Project) AppendEvent(event *es_models.Event) error { +func (p *Project) AppendEvent(event eventstore.Event) error { p.ObjectRoot.AppendEvent(event) - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.ProjectAddedType, project.ProjectChangedType: return p.AppendAddProjectEvent(event) case project.ProjectDeactivatedType: @@ -78,7 +76,7 @@ func (p *Project) AppendEvent(event *es_models.Event) error { return nil } -func (p *Project) AppendAddProjectEvent(event *es_models.Event) error { +func (p *Project) AppendAddProjectEvent(event eventstore.Event) error { p.SetData(event) p.State = int32(model.ProjectStateActive) return nil @@ -99,18 +97,18 @@ func (p *Project) appendRemovedEvent() error { return nil } -func (p *Project) appendOIDCConfig(event *es_models.Event) error { +func (p *Project) appendOIDCConfig(event eventstore.Event) error { appEvent := new(oidcApp) - if err := json.Unmarshal(event.Data, appEvent); err != nil { + if err := event.Unmarshal(p); err != nil { return err } p.OIDCApplications = append(p.OIDCApplications, appEvent) return nil } -func (p *Project) appendApplicationRemoved(event *es_models.Event) error { +func (p *Project) appendApplicationRemoved(event eventstore.Event) error { appEvent := new(oidcApp) - if err := json.Unmarshal(event.Data, appEvent); err != nil { + if err := event.Unmarshal(appEvent); err != nil { return err } for i := len(p.OIDCApplications) - 1; i >= 0; i-- { @@ -124,8 +122,8 @@ func (p *Project) appendApplicationRemoved(event *es_models.Event) error { return nil } -func (p *Project) SetData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, p); err != nil { +func (p *Project) SetData(event eventstore.Event) error { + if err := event.Unmarshal(p); err != nil { logging.Log("EVEN-lo9sr").WithError(err).Error("could not unmarshal event data") return err } diff --git a/internal/project/repository/eventsourcing/model/project_test.go b/internal/project/repository/eventsourcing/model/project_test.go index 854509815..ad897c513 100644 --- a/internal/project/repository/eventsourcing/model/project_test.go +++ b/internal/project/repository/eventsourcing/model/project_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "testing" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/project/model" "github.com/zitadel/zitadel/internal/repository/project" @@ -11,7 +12,7 @@ import ( func TestProjectFromEvents(t *testing.T) { type args struct { - event []*es_models.Event + event []eventstore.Event project *Project } tests := []struct { @@ -22,8 +23,8 @@ func TestProjectFromEvents(t *testing.T) { { name: "project from events, ok", args: args{ - event: []*es_models.Event{ - {AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectAddedType)}, + event: []eventstore.Event{ + &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectAddedType}, }, project: &Project{Name: "ProjectName"}, }, @@ -32,8 +33,8 @@ func TestProjectFromEvents(t *testing.T) { { name: "project from events, nil project", args: args{ - event: []*es_models.Event{ - {AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectAddedType)}, + event: []eventstore.Event{ + &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectAddedType}, }, project: nil, }, @@ -44,7 +45,7 @@ func TestProjectFromEvents(t *testing.T) { t.Run(tt.name, func(t *testing.T) { if tt.args.project != nil { data, _ := json.Marshal(tt.args.project) - tt.args.event[0].Data = data + tt.args.event[0].(*es_models.Event).Data = data } result, _ := ProjectFromEvents(tt.args.project, tt.args.event...) if result.Name != tt.result.Name { @@ -67,7 +68,7 @@ func TestAppendEvent(t *testing.T) { { name: "append added event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectAddedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectAddedType}, project: &Project{Name: "ProjectName"}, }, result: &Project{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, State: int32(model.ProjectStateActive), Name: "ProjectName"}, @@ -75,7 +76,7 @@ func TestAppendEvent(t *testing.T) { { name: "append change event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectChangedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectChangedType}, project: &Project{Name: "ProjectName"}, }, result: &Project{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, State: int32(model.ProjectStateActive), Name: "ProjectName"}, @@ -83,14 +84,14 @@ func TestAppendEvent(t *testing.T) { { name: "append deactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectDeactivatedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectDeactivatedType}, }, result: &Project{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, State: int32(model.ProjectStateInactive)}, }, { name: "append reactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectReactivatedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectReactivatedType}, }, result: &Project{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, State: int32(model.ProjectStateActive)}, }, diff --git a/internal/project/repository/view/model/application.go b/internal/project/repository/view/model/application.go index f7cfd3b28..8a7dc6db6 100644 --- a/internal/project/repository/view/model/application.go +++ b/internal/project/repository/view/model/application.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/project/model" "github.com/zitadel/zitadel/internal/repository/project" @@ -37,25 +36,25 @@ type ApplicationView struct { HasProjectCheck bool `json:"hasProjectCheck" gorm:"column:has_project_check"` PrivateLabelingSetting domain.PrivateLabelingSetting `json:"privateLabelingSetting" gorm:"column:private_labeling_setting"` - IsOIDC bool `json:"-" gorm:"column:is_oidc"` - OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"` - OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` - OIDCRedirectUris database.StringArray `json:"redirectUris" gorm:"column:oidc_redirect_uris"` - OIDCResponseTypes database.EnumArray[domain.OIDCResponseType] `json:"responseTypes" gorm:"column:oidc_response_types"` - OIDCGrantTypes database.EnumArray[domain.OIDCGrantType] `json:"grantTypes" gorm:"column:oidc_grant_types"` - OIDCApplicationType int32 `json:"applicationType" gorm:"column:oidc_application_type"` - OIDCAuthMethodType int32 `json:"authMethodType" gorm:"column:oidc_auth_method_type"` - OIDCPostLogoutRedirectUris database.StringArray `json:"postLogoutRedirectUris" gorm:"column:oidc_post_logout_redirect_uris"` - NoneCompliant bool `json:"-" gorm:"column:none_compliant"` - ComplianceProblems database.StringArray `json:"-" gorm:"column:compliance_problems"` - DevMode bool `json:"devMode" gorm:"column:dev_mode"` - OriginAllowList database.StringArray `json:"-" gorm:"column:origin_allow_list"` - AdditionalOrigins database.StringArray `json:"additionalOrigins" gorm:"column:additional_origins"` - AccessTokenType int32 `json:"accessTokenType" gorm:"column:access_token_type"` - AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion" gorm:"column:access_token_role_assertion"` - IDTokenRoleAssertion bool `json:"idTokenRoleAssertion" gorm:"column:id_token_role_assertion"` - IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion" gorm:"column:id_token_userinfo_assertion"` - ClockSkew time.Duration `json:"clockSkew" gorm:"column:clock_skew"` + IsOIDC bool `json:"-" gorm:"column:is_oidc"` + OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"` + OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"` + OIDCRedirectUris database.TextArray[string] `json:"redirectUris" gorm:"column:oidc_redirect_uris"` + OIDCResponseTypes database.Array[domain.OIDCResponseType] `json:"responseTypes" gorm:"column:oidc_response_types"` + OIDCGrantTypes database.Array[domain.OIDCGrantType] `json:"grantTypes" gorm:"column:oidc_grant_types"` + OIDCApplicationType int32 `json:"applicationType" gorm:"column:oidc_application_type"` + OIDCAuthMethodType int32 `json:"authMethodType" gorm:"column:oidc_auth_method_type"` + OIDCPostLogoutRedirectUris database.TextArray[string] `json:"postLogoutRedirectUris" gorm:"column:oidc_post_logout_redirect_uris"` + NoneCompliant bool `json:"-" gorm:"column:none_compliant"` + ComplianceProblems database.TextArray[string] `json:"-" gorm:"column:compliance_problems"` + DevMode bool `json:"devMode" gorm:"column:dev_mode"` + OriginAllowList database.TextArray[string] `json:"-" gorm:"column:origin_allow_list"` + AdditionalOrigins database.TextArray[string] `json:"additionalOrigins" gorm:"column:additional_origins"` + AccessTokenType int32 `json:"accessTokenType" gorm:"column:access_token_type"` + AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion" gorm:"column:access_token_role_assertion"` + IDTokenRoleAssertion bool `json:"idTokenRoleAssertion" gorm:"column:id_token_role_assertion"` + IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion" gorm:"column:id_token_userinfo_assertion"` + ClockSkew time.Duration `json:"clockSkew" gorm:"column:clock_skew"` IsSAML bool `json:"-" gorm:"column:is_saml"` Metadata []byte `json:"metadata" gorm:"column:metadata"` @@ -82,7 +81,7 @@ func OIDCGrantTypesToModel(granttypes []domain.OIDCGrantType) []model.OIDCGrantT func (a *ApplicationView) AppendEventIfMyApp(event *models.Event) (err error) { view := new(ApplicationView) - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.ApplicationAddedType: err = view.SetData(event) if err != nil { @@ -120,9 +119,9 @@ func (a *ApplicationView) AppendEventIfMyApp(event *models.Event) (err error) { } func (a *ApplicationView) AppendEvent(event *models.Event) (err error) { - a.Sequence = event.Sequence + a.Sequence = event.Seq a.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.ApplicationAddedType: a.setRootData(event) a.CreationDate = event.CreationDate @@ -180,7 +179,7 @@ func (a *ApplicationView) SetData(event *models.Event) error { } func (a *ApplicationView) setOriginAllowList() error { - allowList := make(database.StringArray, 0) + allowList := make(database.TextArray[string], 0) for _, redirect := range a.OIDCRedirectUris { origin, err := http_util.GetOriginFromURLString(redirect) if err != nil { diff --git a/internal/project/repository/view/model/application_test.go b/internal/project/repository/view/model/application_test.go index 30d5a1415..f37e6386b 100644 --- a/internal/project/repository/view/model/application_test.go +++ b/internal/project/repository/view/model/application_test.go @@ -33,7 +33,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append added app event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ApplicationAddedType), Data: mockAppData(&es_model.Application{Name: "AppName"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ApplicationAddedType, Data: mockAppData(&es_model.Application{Name: "AppName"})}, app: &ApplicationView{}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateActive)}, @@ -41,7 +41,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append changed app event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ApplicationChangedType), Data: mockAppData(&es_model.Application{Name: "AppNameChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ApplicationChangedType, Data: mockAppData(&es_model.Application{Name: "AppNameChanged"})}, app: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateActive)}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppNameChanged", State: int32(model.AppStateActive)}, @@ -49,7 +49,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append deactivate app event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ApplicationDeactivatedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ApplicationDeactivatedType}, app: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateActive)}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateInactive)}, @@ -57,7 +57,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append reactivate app event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ApplicationReactivatedType)}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ApplicationReactivatedType}, app: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateInactive)}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateActive)}, @@ -65,7 +65,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append added oidc config event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.OIDCConfigAddedType), Data: mockOIDCConfigData(&es_model.OIDCConfig{ClientID: "clientID"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.OIDCConfigAddedType, Data: mockOIDCConfigData(&es_model.OIDCConfig{ClientID: "clientID"})}, app: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", State: int32(model.AppStateActive)}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", IsOIDC: true, OIDCClientID: "clientID", State: int32(model.AppStateActive)}, @@ -73,7 +73,7 @@ func TestApplicationAppendEvent(t *testing.T) { { name: "append changed oidc config event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.OIDCConfigAddedType), Data: mockOIDCConfigData(&es_model.OIDCConfig{ClientID: "clientIDChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.OIDCConfigAddedType, Data: mockOIDCConfigData(&es_model.OIDCConfig{ClientID: "clientIDChanged"})}, app: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", OIDCClientID: "clientID", State: int32(model.AppStateActive)}, }, result: &ApplicationView{ProjectID: "AggregateID", Name: "AppName", IsOIDC: true, OIDCClientID: "clientIDChanged", State: int32(model.AppStateActive)}, diff --git a/internal/project/repository/view/model/project.go b/internal/project/repository/view/model/project.go index d1b4e0130..81ccb4929 100644 --- a/internal/project/repository/view/model/project.go +++ b/internal/project/repository/view/model/project.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/project/model" "github.com/zitadel/zitadel/internal/repository/project" @@ -36,8 +35,8 @@ type ProjectView struct { func (p *ProjectView) AppendEvent(event *models.Event) (err error) { p.ChangeDate = event.CreationDate - p.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { + p.Sequence = event.Seq + switch event.Type() { case project.ProjectAddedType: p.State = int32(model.ProjectStateActive) p.CreationDate = event.CreationDate @@ -70,11 +69,7 @@ func (p *ProjectView) setData(event *models.Event) error { func (p *ProjectView) setProjectData(event *models.Event) error { project := new(ProjectView) - err := project.SetData(event) - if err != nil { - return err - } - return nil + return project.SetData(event) } func (p *ProjectView) SetData(event *models.Event) error { diff --git a/internal/project/repository/view/model/project_grant.go b/internal/project/repository/view/model/project_grant.go index 399e82a61..a8a14113f 100644 --- a/internal/project/repository/view/model/project_grant.go +++ b/internal/project/repository/view/model/project_grant.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/project/model" "github.com/zitadel/zitadel/internal/repository/project" @@ -24,18 +23,18 @@ const ( ) type ProjectGrantView struct { - GrantID string `json:"-" gorm:"column:grant_id;primary_key"` - ProjectID string `json:"-" gorm:"column:project_id"` - OrgID string `json:"-" gorm:"column:org_id"` - Name string `json:"name" gorm:"column:project_name"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - State int32 `json:"-" gorm:"column:project_state"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"` - OrgName string `json:"-" gorm:"column:org_name"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - GrantedRoleKeys database.StringArray `json:"-" gorm:"column:granted_role_keys"` + GrantID string `json:"-" gorm:"column:grant_id;primary_key"` + ProjectID string `json:"-" gorm:"column:project_id"` + OrgID string `json:"-" gorm:"column:org_id"` + Name string `json:"name" gorm:"column:project_name"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + State int32 `json:"-" gorm:"column:project_state"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"` + OrgName string `json:"-" gorm:"column:org_name"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + GrantedRoleKeys database.TextArray[string] `json:"-" gorm:"column:granted_role_keys"` } type ProjectGrant struct { @@ -47,8 +46,8 @@ type ProjectGrant struct { func (p *ProjectGrantView) AppendEvent(event *models.Event) (err error) { p.ChangeDate = event.CreationDate - p.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { + p.Sequence = event.Seq + switch event.Type() { case project.GrantAddedType: p.State = int32(model.ProjectStateActive) p.CreationDate = event.CreationDate diff --git a/internal/project/repository/view/model/project_grant_member.go b/internal/project/repository/view/model/project_grant_member.go index 1c1702261..7e52a8804 100644 --- a/internal/project/repository/view/model/project_grant_member.go +++ b/internal/project/repository/view/model/project_grant_member.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/project" ) @@ -24,28 +23,28 @@ const ( ) type ProjectGrantMemberView struct { - UserID string `json:"userId" gorm:"column:user_id;primary_key"` - GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"` - ProjectID string `json:"-" gorm:"column:project_id"` - UserName string `json:"-" gorm:"column:user_name"` - Email string `json:"-" gorm:"column:email_address"` - FirstName string `json:"-" gorm:"column:first_name"` - LastName string `json:"-" gorm:"column:last_name"` - DisplayName string `json:"-" gorm:"column:display_name"` - Roles database.StringArray `json:"roles" gorm:"column:roles"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` - AvatarKey string `json:"-" gorm:"column:avatar_key"` - UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` + UserID string `json:"userId" gorm:"column:user_id;primary_key"` + GrantID string `json:"grantId" gorm:"column:grant_id;primary_key"` + ProjectID string `json:"-" gorm:"column:project_id"` + UserName string `json:"-" gorm:"column:user_name"` + Email string `json:"-" gorm:"column:email_address"` + FirstName string `json:"-" gorm:"column:first_name"` + LastName string `json:"-" gorm:"column:last_name"` + DisplayName string `json:"-" gorm:"column:display_name"` + Roles database.TextArray[string] `json:"roles" gorm:"column:roles"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` + AvatarKey string `json:"-" gorm:"column:avatar_key"` + UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` CreationDate time.Time `json:"-" gorm:"column:creation_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"` } func (r *ProjectGrantMemberView) AppendEvent(event *models.Event) (err error) { - r.Sequence = event.Sequence + r.Sequence = event.Seq r.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.GrantMemberAddedType: r.setRootData(event) r.CreationDate = event.CreationDate diff --git a/internal/project/repository/view/model/project_grant_member_test.go b/internal/project/repository/view/model/project_grant_member_test.go index ca9d19be7..57028979a 100644 --- a/internal/project/repository/view/model/project_grant_member_test.go +++ b/internal/project/repository/view/model/project_grant_member_test.go @@ -28,7 +28,7 @@ func TestGrantedProjectMemberAppendEvent(t *testing.T) { { name: "append added member event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", UserID: "UserID", Roles: []string{"Role"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantMemberAddedType, ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", UserID: "UserID", Roles: []string{"Role"}})}, member: &ProjectGrantMemberView{}, }, result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"Role"}}, @@ -36,7 +36,7 @@ func TestGrantedProjectMemberAppendEvent(t *testing.T) { { name: "append changed member event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantMemberAddedType), ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", Roles: []string{"RoleChanged"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantMemberAddedType, ResourceOwner: "OrgID", Data: mockProjectGrantMemberData(&es_model.ProjectGrantMember{GrantID: "ProjectGrantID", Roles: []string{"RoleChanged"}})}, member: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"Role"}}, }, result: &ProjectGrantMemberView{ProjectID: "AggregateID", UserID: "UserID", GrantID: "ProjectGrantID", Roles: []string{"RoleChanged"}}, diff --git a/internal/project/repository/view/model/project_grant_test.go b/internal/project/repository/view/model/project_grant_test.go index 9aca2dd92..3f777182b 100644 --- a/internal/project/repository/view/model/project_grant_test.go +++ b/internal/project/repository/view/model/project_grant_test.go @@ -34,7 +34,7 @@ func TestProjectGrantAppendEvent(t *testing.T) { { name: "append added project grant event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantAddedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", GrantedOrgID: "GrantedOrgID", RoleKeys: []string{"Role"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantAddedType, ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", GrantedOrgID: "GrantedOrgID", RoleKeys: []string{"Role"}})}, project: &ProjectGrantView{}, }, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}}, @@ -42,7 +42,7 @@ func TestProjectGrantAppendEvent(t *testing.T) { { name: "append change project grant event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantChangedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", RoleKeys: []string{"RoleChanged"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantChangedType, ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID", RoleKeys: []string{"RoleChanged"}})}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}}, }, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"RoleChanged"}}, @@ -50,7 +50,7 @@ func TestProjectGrantAppendEvent(t *testing.T) { { name: "append deactivate project grant event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantDeactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantDeactivatedType, ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}}, }, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: []string{"Role"}}, @@ -58,7 +58,7 @@ func TestProjectGrantAppendEvent(t *testing.T) { { name: "append reactivate project grant event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.GrantReactivatedType), ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.GrantReactivatedType, ResourceOwner: "GrantedOrgID", Data: mockProjectGrantData(&es_model.ProjectGrant{GrantID: "ProjectGrantID"})}, project: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateInactive), GrantedRoleKeys: []string{"Role"}}, }, result: &ProjectGrantView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", OrgID: "GrantedOrgID", State: int32(model.ProjectStateActive), GrantedRoleKeys: []string{"Role"}}, diff --git a/internal/project/repository/view/model/project_member.go b/internal/project/repository/view/model/project_member.go index 1cf664a8d..4a20e3da1 100644 --- a/internal/project/repository/view/model/project_member.go +++ b/internal/project/repository/view/model/project_member.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/project" ) @@ -23,27 +22,27 @@ const ( ) type ProjectMemberView struct { - UserID string `json:"userId" gorm:"column:user_id;primary_key"` - ProjectID string `json:"-" gorm:"column:project_id;primary_key"` - UserName string `json:"-" gorm:"column:user_name"` - Email string `json:"-" gorm:"column:email_address"` - FirstName string `json:"-" gorm:"column:first_name"` - LastName string `json:"-" gorm:"column:last_name"` - DisplayName string `json:"-" gorm:"column:display_name"` - Roles database.StringArray `json:"roles" gorm:"column:roles"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` - AvatarKey string `json:"-" gorm:"column:avatar_key"` - UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` + UserID string `json:"userId" gorm:"column:user_id;primary_key"` + ProjectID string `json:"-" gorm:"column:project_id;primary_key"` + UserName string `json:"-" gorm:"column:user_name"` + Email string `json:"-" gorm:"column:email_address"` + FirstName string `json:"-" gorm:"column:first_name"` + LastName string `json:"-" gorm:"column:last_name"` + DisplayName string `json:"-" gorm:"column:display_name"` + Roles database.TextArray[string] `json:"roles" gorm:"column:roles"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` + AvatarKey string `json:"-" gorm:"column:avatar_key"` + UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"` CreationDate time.Time `json:"-" gorm:"column:creation_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"` } func (r *ProjectMemberView) AppendEvent(event *models.Event) (err error) { - r.Sequence = event.Sequence + r.Sequence = event.Seq r.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { + switch event.Type() { case project.MemberAddedType: r.setRootData(event) r.CreationDate = event.CreationDate diff --git a/internal/project/repository/view/model/project_member_test.go b/internal/project/repository/view/model/project_member_test.go index 0dbee2535..dff842566 100644 --- a/internal/project/repository/view/model/project_member_test.go +++ b/internal/project/repository/view/model/project_member_test.go @@ -28,7 +28,7 @@ func TestProjectMemberAppendEvent(t *testing.T) { { name: "append added member event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"Role"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.MemberAddedType, ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"Role"}})}, member: &ProjectMemberView{}, }, result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"Role"}}, @@ -36,7 +36,7 @@ func TestProjectMemberAppendEvent(t *testing.T) { { name: "append changed member event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.MemberAddedType), ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"RoleChanged"}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.MemberAddedType, ResourceOwner: "OrgID", Data: mockProjectMemberData(&es_model.ProjectMember{UserID: "UserID", Roles: []string{"RoleChanged"}})}, member: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"Role"}}, }, result: &ProjectMemberView{ProjectID: "AggregateID", UserID: "UserID", Roles: []string{"RoleChanged"}}, diff --git a/internal/project/repository/view/model/project_test.go b/internal/project/repository/view/model/project_test.go index a1c62fadc..4731d98f2 100644 --- a/internal/project/repository/view/model/project_test.go +++ b/internal/project/repository/view/model/project_test.go @@ -22,7 +22,7 @@ func TestProjectAppendEvent(t *testing.T) { { name: "append added project event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectAddedType), ResourceOwner: "GrantedOrgID", Data: mockProjectData(&es_model.Project{Name: "ProjectName"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectAddedType, ResourceOwner: "GrantedOrgID", Data: mockProjectData(&es_model.Project{Name: "ProjectName"})}, project: &ProjectView{}, }, result: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateActive)}, @@ -30,7 +30,7 @@ func TestProjectAppendEvent(t *testing.T) { { name: "append change project event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectChangedType), ResourceOwner: "GrantedOrgID", Data: mockProjectData(&es_model.Project{Name: "ProjectNameChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectChangedType, ResourceOwner: "GrantedOrgID", Data: mockProjectData(&es_model.Project{Name: "ProjectNameChanged"})}, project: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateActive)}, }, result: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectNameChanged", State: int32(model.ProjectStateActive)}, @@ -38,7 +38,7 @@ func TestProjectAppendEvent(t *testing.T) { { name: "append project deactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectDeactivatedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectDeactivatedType, ResourceOwner: "GrantedOrgID"}, project: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateActive)}, }, result: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateInactive)}, @@ -46,7 +46,7 @@ func TestProjectAppendEvent(t *testing.T) { { name: "append project reactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(project.ProjectReactivatedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: project.ProjectReactivatedType, ResourceOwner: "GrantedOrgID"}, project: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateInactive)}, }, result: &ProjectView{ProjectID: "AggregateID", ResourceOwner: "GrantedOrgID", Name: "ProjectName", State: int32(model.ProjectStateActive)}, diff --git a/internal/project/repository/view/query.go b/internal/project/repository/view/query.go index 56c8ae007..70d7f29e4 100644 --- a/internal/project/repository/view/query.go +++ b/internal/project/repository/view/query.go @@ -2,28 +2,29 @@ package view import ( "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/project" ) -func ProjectByIDQuery(id, instanceID string, latestSequence uint64) (*es_models.SearchQuery, error) { +func ProjectByIDQuery(id, instanceID string, latestSequence uint64) (*eventstore.SearchQueryBuilder, error) { if id == "" { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dke74", "Errors.Project.ProjectIDMissing") } - return es_models.NewSearchQuery(). + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + InstanceID(instanceID). + AwaitOpenTransactions(). + SequenceGreater(latestSequence). AddQuery(). - AggregateIDFilter(id). - AggregateTypeFilter(project.AggregateType). - LatestSequenceFilter(latestSequence). - InstanceIDFilter(instanceID). - EventTypesFilter( - es_models.EventType(project.ProjectAddedType), - es_models.EventType(project.ProjectChangedType), - es_models.EventType(project.ProjectDeactivatedType), - es_models.EventType(project.ProjectReactivatedType), - es_models.EventType(project.ProjectRemovedType), - es_models.EventType(project.OIDCConfigAddedType), - es_models.EventType(project.ApplicationRemovedType), + AggregateTypes(project.AggregateType). + AggregateIDs(id). + EventTypes( + project.ProjectAddedType, + project.ProjectChangedType, + project.ProjectDeactivatedType, + project.ProjectReactivatedType, + project.ProjectRemovedType, + project.OIDCConfigAddedType, + project.ApplicationRemovedType, ). - SearchQuery(), nil + Builder(), nil } diff --git a/internal/query/access_token.go b/internal/query/access_token.go index 25121af05..664796d2d 100644 --- a/internal/query/access_token.go +++ b/internal/query/access_token.go @@ -54,6 +54,7 @@ func (wm *OIDCSessionAccessTokenReadModel) Reduce() error { func (wm *OIDCSessionAccessTokenReadModel) Query() *eventstore.SearchQueryBuilder { return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). AllowTimeTravel(). AddQuery(). AggregateTypes(oidcsession.AggregateType). @@ -86,7 +87,7 @@ func (wm *OIDCSessionAccessTokenReadModel) reduceAccessTokenAdded(e *oidcsession func (wm *OIDCSessionAccessTokenReadModel) reduceTokenRevoked(e eventstore.Event) { wm.AccessTokenID = "" - wm.AccessTokenExpiration = e.CreationDate() + wm.AccessTokenExpiration = e.CreatedAt() } // ActiveAccessTokenByToken will check if the token is active by retrieving the OIDCSession events from the eventstore. @@ -133,14 +134,15 @@ func (q *Queries) checkSessionNotTerminatedAfter(ctx context.Context, sessionID defer func() { span.EndWithError(err) }() events, err := q.eventstore.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). AllowTimeTravel(). + CreationDateAfter(creation). AddQuery(). AggregateTypes(session.AggregateType). AggregateIDs(sessionID). EventTypes( session.TerminateType, ). - CreationDateAfter(creation). Builder()) if err != nil { return caos_errs.ThrowPermissionDenied(err, "QUERY-SJ642", "Errors.Internal") diff --git a/internal/query/action.go b/internal/query/action.go index 26aee576e..e2c75e690 100644 --- a/internal/query/action.go +++ b/internal/query/action.go @@ -138,7 +138,7 @@ func (q *Queries) SearchActions(ctx context.Context, queries *ActionSearchQuerie return nil, errors.ThrowInternal(err, "QUERY-SDfr52", "Errors.Internal") } - actions.LatestSequence, err = q.latestSequence(ctx, actionTable) + actions.State, err = q.latestState(ctx, actionTable) return actions, err } diff --git a/internal/query/app.go b/internal/query/app.go index 639b2e141..48c05bd15 100644 --- a/internal/query/app.go +++ b/internal/query/app.go @@ -14,6 +14,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -40,23 +41,23 @@ type App struct { } type OIDCApp struct { - RedirectURIs database.StringArray - ResponseTypes database.EnumArray[domain.OIDCResponseType] - GrantTypes database.EnumArray[domain.OIDCGrantType] + RedirectURIs database.TextArray[string] + ResponseTypes database.Array[domain.OIDCResponseType] + GrantTypes database.Array[domain.OIDCGrantType] AppType domain.OIDCApplicationType ClientID string AuthMethodType domain.OIDCAuthMethodType - PostLogoutRedirectURIs database.StringArray + PostLogoutRedirectURIs database.TextArray[string] Version domain.OIDCVersion - ComplianceProblems database.StringArray + ComplianceProblems database.TextArray[string] IsDevMode bool AccessTokenType domain.OIDCTokenType AssertAccessTokenRole bool AssertIDTokenRole bool AssertIDTokenUserinfo bool ClockSkew time.Duration - AdditionalOrigins database.StringArray - AllowedOrigins database.StringArray + AdditionalOrigins database.TextArray[string] + AllowedOrigins database.TextArray[string] SkipNativeAppSuccessPage bool } @@ -253,7 +254,8 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.AppProjection.Trigger(ctx) + ctx, err = projection.AppProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := prepareAppQuery(ctx, q.client) @@ -502,7 +504,7 @@ func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, wit if err != nil { return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal") } - apps.LatestSequence, err = q.latestSequence(ctx, appsTable) + apps.State, err = q.latestState(ctx, appsTable) return apps, err } @@ -630,7 +632,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, if errs.Is(err, sql.ErrNoRows) { return nil, errors.ThrowNotFound(err, "QUERY-pCP8P", "Errors.App.NotExisting") } - return nil, errors.ThrowInternal(err, "QUERY-0R2Nw", "Errors.Internal") + return nil, errors.ThrowInternal(err, "QUERY-4SJlx", "Errors.Internal") } apiConfig.set(app) @@ -700,7 +702,7 @@ func prepareProjectByAppQuery(ctx context.Context, db prepareDatabase) (sq.Selec ) if err != nil { if errs.Is(err, sql.ErrNoRows) { - return nil, errors.ThrowNotFound(err, "QUERY-fk2fs", "Errors.Project.NotFound") + return nil, errors.ThrowNotFound(err, "QUERY-yxTMh", "Errors.Project.NotFound") } return nil, errors.ThrowInternal(err, "QUERY-dj2FF", "Errors.Internal") } @@ -802,7 +804,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder ) if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-0R2Nw", "Errors.Internal") + return nil, errors.ThrowInternal(err, "QUERY-XGWAX", "Errors.Internal") } apiConfig.set(app) @@ -824,7 +826,7 @@ func prepareClientIDsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)). LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), func(rows *sql.Rows) ([]string, error) { - ids := database.StringArray{} + ids := database.TextArray[string]{} for rows.Next() { var apiID sql.NullString @@ -850,19 +852,19 @@ type sqlOIDCConfig struct { appID sql.NullString version sql.NullInt32 clientID sql.NullString - redirectUris database.StringArray + redirectUris database.TextArray[string] applicationType sql.NullInt16 authMethodType sql.NullInt16 - postLogoutRedirectUris database.StringArray + postLogoutRedirectUris database.TextArray[string] devMode sql.NullBool accessTokenType sql.NullInt16 accessTokenRoleAssertion sql.NullBool iDTokenRoleAssertion sql.NullBool iDTokenUserinfoAssertion sql.NullBool clockSkew sql.NullInt64 - additionalOrigins database.StringArray - responseTypes database.EnumArray[domain.OIDCResponseType] - grantTypes database.EnumArray[domain.OIDCGrantType] + additionalOrigins database.TextArray[string] + responseTypes database.Array[domain.OIDCResponseType] + grantTypes database.Array[domain.OIDCGrantType] skipNativeAppSuccessPage sql.NullBool } diff --git a/internal/query/app_test.go b/internal/query/app_test.go index ca93baddf..b98d43a78 100644 --- a/internal/query/app_test.go +++ b/internal/query/app_test.go @@ -126,7 +126,7 @@ var ( ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - appCols = database.StringArray{ + appCols = database.TextArray[string]{ "id", "name", "project_id", @@ -420,19 +420,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -460,21 +460,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -506,19 +506,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, false, domain.OIDCTokenTypeJWT, false, false, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -546,21 +546,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: false, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: false, AssertIDTokenRole: false, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -592,19 +592,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, false, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -632,21 +632,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: false, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -678,19 +678,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, false, domain.OIDCTokenTypeJWT, false, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -718,21 +718,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: false, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: false, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -764,19 +764,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, false, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -804,21 +804,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: false, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -850,19 +850,19 @@ func Test_AppsPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeNative, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, false, domain.OIDCTokenTypeJWT, false, false, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, true, // saml config nil, @@ -890,21 +890,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeNative, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: false, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: false, AssertIDTokenRole: false, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: true, }, }, @@ -936,19 +936,19 @@ func Test_AppsPrepare(t *testing.T) { "oidc-app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1050,21 +1050,21 @@ func Test_AppsPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1299,19 +1299,19 @@ func Test_AppPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1334,21 +1334,21 @@ func Test_AppPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1441,19 +1441,19 @@ func Test_AppPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, false, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1476,21 +1476,21 @@ func Test_AppPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: false, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1520,19 +1520,19 @@ func Test_AppPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, false, true, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1555,21 +1555,21 @@ func Test_AppPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: false, AssertIDTokenRole: true, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1599,19 +1599,19 @@ func Test_AppPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, false, true, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1634,21 +1634,21 @@ func Test_AppPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: false, AssertIDTokenUserinfo: true, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1678,19 +1678,19 @@ func Test_AppPrepare(t *testing.T) { "app-id", domain.OIDCVersionV1, "oidc-client-id", - database.StringArray{"https://redirect.to/me"}, - database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + database.TextArray[string]{"https://redirect.to/me"}, + database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, domain.OIDCApplicationTypeUserAgent, domain.OIDCAuthMethodTypeNone, - database.StringArray{"post.logout.ch"}, + database.TextArray[string]{"post.logout.ch"}, true, domain.OIDCTokenTypeJWT, true, true, false, 1 * time.Second, - database.StringArray{"additional.origin"}, + database.TextArray[string]{"additional.origin"}, false, // saml config nil, @@ -1713,21 +1713,21 @@ func Test_AppPrepare(t *testing.T) { OIDCConfig: &OIDCApp{ Version: domain.OIDCVersionV1, ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + RedirectURIs: database.TextArray[string]{"https://redirect.to/me"}, + ResponseTypes: database.Array[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.Array[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, AppType: domain.OIDCApplicationTypeUserAgent, AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"}, IsDevMode: true, AccessTokenType: domain.OIDCTokenTypeJWT, AssertAccessTokenRole: true, AssertIDTokenRole: true, AssertIDTokenUserinfo: false, ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, + AdditionalOrigins: database.TextArray[string]{"additional.origin"}, ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"}, SkipNativeAppSuccessPage: false, }, }, @@ -1786,7 +1786,7 @@ func Test_AppIDsPrepare(t *testing.T) { want: want{ sqlExpectations: mockQueries( expectedAppIDsQuery, - database.StringArray{"client_id", "client_id"}, + database.TextArray[string]{"client_id", "client_id"}, [][]driver.Value{ { "app-id", @@ -1803,7 +1803,7 @@ func Test_AppIDsPrepare(t *testing.T) { want: want{ sqlExpectations: mockQueries( expectedAppIDsQuery, - database.StringArray{"client_id", "client_id"}, + database.TextArray[string]{"client_id", "client_id"}, [][]driver.Value{ { nil, @@ -1878,7 +1878,7 @@ func Test_ProjectIDByAppPrepare(t *testing.T) { want: want{ sqlExpectations: mockQuery( expectedProjectIDByAppQuery, - database.StringArray{"project_id"}, + database.TextArray[string]{"project_id"}, []driver.Value{"project-id"}, ), }, diff --git a/internal/query/auth_request.go b/internal/query/auth_request.go index 334c101c9..2f20ec387 100644 --- a/internal/query/auth_request.go +++ b/internal/query/auth_request.go @@ -8,11 +8,14 @@ import ( "fmt" "time" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -50,13 +53,14 @@ func (q *Queries) AuthRequestByID(ctx context.Context, shouldTriggerBulk bool, i defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.AuthRequestProjection.Trigger(ctx) + ctx, err = projection.AuthRequestProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } var ( - scope database.StringArray - prompt database.EnumArray[domain.Prompt] - locales database.StringArray + scope database.TextArray[string] + prompt database.Array[domain.Prompt] + locales database.TextArray[string] ) dst := new(AuthRequest) diff --git a/internal/query/auth_request_test.go b/internal/query/auth_request_test.go index ba23b7315..4cc52fa8c 100644 --- a/internal/query/auth_request_test.go +++ b/internal/query/auth_request_test.go @@ -63,10 +63,10 @@ func TestQueries_AuthRequestByID(t *testing.T) { testNow, "loginClient", "clientID", - database.StringArray{"a", "b", "c"}, + database.TextArray[string]{"a", "b", "c"}, "example.com", - database.EnumArray[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, - database.StringArray{"en", "fi"}, + database.Array[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, + database.TextArray[string]{"en", "fi"}, "me@example.com", int64(time.Minute), "userID", @@ -97,10 +97,10 @@ func TestQueries_AuthRequestByID(t *testing.T) { testNow, "loginClient", "clientID", - database.StringArray{"a", "b", "c"}, + database.TextArray[string]{"a", "b", "c"}, "example.com", - database.EnumArray[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, - database.StringArray{"en", "fi"}, + database.Array[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, + database.TextArray[string]{"en", "fi"}, sql.NullString{}, sql.NullInt64{}, sql.NullString{}, @@ -149,10 +149,10 @@ func TestQueries_AuthRequestByID(t *testing.T) { testNow, "wrongLoginClient", "clientID", - database.StringArray{"a", "b", "c"}, + database.TextArray[string]{"a", "b", "c"}, "example.com", - database.EnumArray[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, - database.StringArray{"en", "fi"}, + database.Array[domain.Prompt]{domain.PromptLogin, domain.PromptConsent}, + database.TextArray[string]{"en", "fi"}, sql.NullString{}, sql.NullInt64{}, sql.NullString{}, diff --git a/internal/query/authn_key.go b/internal/query/authn_key.go index 2aa3576ad..2022a94ce 100644 --- a/internal/query/authn_key.go +++ b/internal/query/authn_key.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -152,7 +155,7 @@ func (q *Queries) SearchAuthNKeys(ctx context.Context, queries *AuthNKeySearchQu return nil, errors.ThrowInternal(err, "QUERY-Dbg53", "Errors.Internal") } - authNKeys.LatestSequence, err = q.latestSequence(ctx, authNKeyTable) + authNKeys.State, err = q.latestState(ctx, authNKeyTable) return authNKeys, err } @@ -181,7 +184,7 @@ func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySear if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Dbi53", "Errors.Internal") } - authNKeys.LatestSequence, err = q.latestSequence(ctx, authNKeyTable) + authNKeys.State, err = q.latestState(ctx, authNKeyTable) return authNKeys, err } @@ -190,7 +193,8 @@ func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, i defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.AuthNKeyProjection.Trigger(ctx) + ctx, err = projection.AuthNKeyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := prepareAuthNKeyQuery(ctx, q.client) diff --git a/internal/query/certificate.go b/internal/query/certificate.go index 6b5f06ebf..d43787ff2 100644 --- a/internal/query/certificate.go +++ b/internal/query/certificate.go @@ -96,7 +96,7 @@ func (q *Queries) ActiveCertificates(ctx context.Context, t time.Time, usage dom return nil, errors.ThrowInternal(err, "QUERY-Sgan4", "Errors.Internal") } - certs.LatestSequence, err = q.latestSequence(ctx, keyTable) + certs.State, err = q.latestState(ctx, keyTable) if !errors.IsNotFound(err) { return certs, err } diff --git a/internal/query/current_sequence.go b/internal/query/current_sequence.go deleted file mode 100644 index 50c18a900..000000000 --- a/internal/query/current_sequence.go +++ /dev/null @@ -1,320 +0,0 @@ -package query - -import ( - "context" - "database/sql" - errs "errors" - "fmt" - "time" - - sq "github.com/Masterminds/squirrel" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/api/call" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/query/projection" - "github.com/zitadel/zitadel/internal/telemetry/tracing" -) - -const ( - lockStmtFormat = "UPDATE %[1]s" + - " set locker_id = $1, locked_until = now()+$2::INTERVAL" + - " WHERE projection_name = $3" - lockerIDReset = "reset" -) - -type LatestSequence struct { - Sequence uint64 - Timestamp time.Time -} - -type CurrentSequences struct { - SearchResponse - CurrentSequences []*CurrentSequence -} - -type CurrentSequence struct { - ProjectionName string - CurrentSequence uint64 - Timestamp time.Time -} - -type CurrentSequencesSearchQueries struct { - SearchRequest - Queries []SearchQuery -} - -func NewCurrentSequencesInstanceIDSearchQuery(instanceID string) (SearchQuery, error) { - return NewTextQuery(CurrentSequenceColInstanceID, instanceID, TextEquals) -} - -func (q *CurrentSequencesSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { - query = q.SearchRequest.toQuery(query) - for _, q := range q.Queries { - query = q.toQuery(query) - } - return query -} - -func (q *Queries) SearchCurrentSequences(ctx context.Context, queries *CurrentSequencesSearchQueries) (failedEvents *CurrentSequences, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - query, scan := prepareCurrentSequencesQuery(ctx, q.client) - stmt, args, err := queries.toQuery(query).ToSql() - if err != nil { - return nil, errors.ThrowInvalidArgument(err, "QUERY-MmFef", "Errors.Query.InvalidRequest") - } - - err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { - failedEvents, err = scan(rows) - return err - }, stmt, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-22H8f", "Errors.Internal") - } - return failedEvents, nil -} - -func (q *Queries) latestSequence(ctx context.Context, projections ...table) (seq *LatestSequence, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - query, scan := prepareLatestSequence(ctx, q.client) - or := make(sq.Or, len(projections)) - for i, projection := range projections { - or[i] = sq.Eq{CurrentSequenceColProjectionName.identifier(): projection.name} - } - stmt, args, err := query. - Where(or). - Where(sq.Eq{CurrentSequenceColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}). - OrderBy(CurrentSequenceColCurrentSequence.identifier() + " DESC"). - ToSql() - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement") - } - - err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { - seq, err = scan(row) - return err - }, stmt, args...) - - return seq, err -} - -func (q *Queries) ClearCurrentSequence(ctx context.Context, projectionName string) (err error) { - err = q.checkAndLock(ctx, projectionName) - if err != nil { - return err - } - - tx, err := q.client.Begin() - if err != nil { - return errors.ThrowInternal(err, "QUERY-9iOpr", "Errors.RemoveFailed") - } - tables, err := tablesForReset(ctx, tx, projectionName) - if err != nil { - return err - } - err = reset(tx, tables, projectionName) - if err != nil { - return err - } - return tx.Commit() -} - -func (q *Queries) checkAndLock(ctx context.Context, projectionName string) error { - projectionQuery, args, err := sq.Select("count(*)"). - From("[show tables from projections]"). - Where( - sq.And{ - sq.NotEq{"table_name": []string{"locks", "current_sequences", "failed_events"}}, - sq.Eq{"concat('projections.', table_name)": projectionName}, - }). - PlaceholderFormat(sq.Dollar). - ToSql() - if err != nil { - return errors.ThrowInternal(err, "QUERY-Dfwf2", "Errors.ProjectionName.Invalid") - } - var count int - err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { - if err := row.Scan(&count); err != nil || count == 0 { - return errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") - } - return err - }, projectionQuery, args...) - if err != nil { - return err - } - - lock := fmt.Sprintf(lockStmtFormat, locksTable.identifier()) - if err != nil { - return errors.ThrowInternal(err, "QUERY-DVfg3", "Errors.RemoveFailed") - } - //lock for twice the default duration (10s) - res, err := q.client.ExecContext(ctx, lock, lockerIDReset, 20*time.Second, projectionName) - if err != nil { - return errors.ThrowInternal(err, "QUERY-WEfr2", "Errors.RemoveFailed") - } - rows, err := res.RowsAffected() - if err != nil || rows == 0 { - return errors.ThrowInternal(err, "QUERY-Bh3ws", "Errors.RemoveFailed") - } - time.Sleep(7 * time.Second) //more than half the default lock duration (10s) - return nil -} - -func tablesForReset(ctx context.Context, tx *sql.Tx, projectionName string) ([]string, error) { - tablesQuery, args, err := sq.Select("concat('projections.', table_name)"). - From("[show tables from projections]"). - Where( - sq.And{ - sq.Eq{"type": "table"}, - sq.NotEq{"table_name": []string{"locks", "current_sequences", "failed_events"}}, - sq.Like{"concat('projections.', table_name)": projectionName + "%"}, - }). - PlaceholderFormat(sq.Dollar). - ToSql() - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-ASff2", "Errors.ProjectionName.Invalid") - } - var tables []string - rows, err := tx.QueryContext(ctx, tablesQuery, args...) - if err != nil { - return nil, errors.ThrowInternal(err, "QUERY-Dgfw", "Errors.ProjectionName.Invalid") - } - for rows.Next() { - var tableName string - if err := rows.Scan(&tableName); err != nil { - return nil, errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") - } - tables = append(tables, tableName) - } - return tables, nil -} - -func reset(tx *sql.Tx, tables []string, projectionName string) error { - for _, tableName := range tables { - _, err := tx.Exec(fmt.Sprintf("TRUNCATE %s cascade", tableName)) - if err != nil { - return errors.ThrowInternal(err, "QUERY-3n92f", "Errors.RemoveFailed") - } - } - update, args, err := sq.Update(currentSequencesTable.identifier()). - Set(CurrentSequenceColCurrentSequence.name, 0). - Where(sq.Eq{CurrentSequenceColProjectionName.name: projectionName}). - PlaceholderFormat(sq.Dollar). - ToSql() - if err != nil { - return errors.ThrowInternal(err, "QUERY-Ff3tw", "Errors.RemoveFailed") - } - _, err = tx.Exec(update, args...) - if err != nil { - return errors.ThrowInternal(err, "QUERY-NFiws", "Errors.RemoveFailed") - } - return nil -} - -func prepareLatestSequence(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*LatestSequence, error)) { - return sq.Select( - CurrentSequenceColCurrentSequence.identifier(), - CurrentSequenceColTimestamp.identifier()). - From(currentSequencesTable.identifier() + db.Timetravel(call.Took(ctx))). - PlaceholderFormat(sq.Dollar), - func(row *sql.Row) (*LatestSequence, error) { - seq := new(LatestSequence) - err := row.Scan( - &seq.Sequence, - &seq.Timestamp, - ) - if err != nil && !errs.Is(err, sql.ErrNoRows) { - return nil, errors.ThrowInternal(err, "QUERY-aAZ1D", "Errors.Internal") - } - return seq, nil - } -} - -func prepareCurrentSequencesQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*CurrentSequences, error)) { - return sq.Select( - "max("+CurrentSequenceColCurrentSequence.identifier()+") as "+CurrentSequenceColCurrentSequence.name, - "max("+CurrentSequenceColTimestamp.identifier()+") as "+CurrentSequenceColTimestamp.name, - CurrentSequenceColProjectionName.identifier(), - countColumn.identifier()). - From(currentSequencesTable.identifier() + db.Timetravel(call.Took(ctx))). - GroupBy(CurrentSequenceColProjectionName.identifier()). - PlaceholderFormat(sq.Dollar), - func(rows *sql.Rows) (*CurrentSequences, error) { - currentSequences := make([]*CurrentSequence, 0) - var count uint64 - for rows.Next() { - currentSequence := new(CurrentSequence) - err := rows.Scan( - ¤tSequence.CurrentSequence, - ¤tSequence.Timestamp, - ¤tSequence.ProjectionName, - &count, - ) - if err != nil { - return nil, err - } - currentSequences = append(currentSequences, currentSequence) - } - - if err := rows.Close(); err != nil { - return nil, errors.ThrowInternal(err, "QUERY-jbJ77", "Errors.Query.CloseRows") - } - - return &CurrentSequences{ - CurrentSequences: currentSequences, - SearchResponse: SearchResponse{ - Count: count, - }, - }, nil - } -} - -var ( - currentSequencesTable = table{ - name: projection.CurrentSeqTable, - instanceIDCol: "instance_id", - } - CurrentSequenceColAggregateType = Column{ - name: "aggregate_type", - table: currentSequencesTable, - } - CurrentSequenceColCurrentSequence = Column{ - name: "current_sequence", - table: currentSequencesTable, - } - CurrentSequenceColTimestamp = Column{ - name: "timestamp", - table: currentSequencesTable, - } - CurrentSequenceColProjectionName = Column{ - name: "projection_name", - table: currentSequencesTable, - } - CurrentSequenceColInstanceID = Column{ - name: "instance_id", - table: currentSequencesTable, - } -) - -var ( - locksTable = table{ - name: projection.LocksTable, - instanceIDCol: "instance_id", - } - LocksColLockerID = Column{ - name: "locker_id", - table: locksTable, - } - LocksColUntil = Column{ - name: "locked_until", - table: locksTable, - } - LocksColProjectionName = Column{ - name: "projection_name", - table: locksTable, - } -) diff --git a/internal/query/current_state.go b/internal/query/current_state.go new file mode 100644 index 000000000..2c42e97bd --- /dev/null +++ b/internal/query/current_state.go @@ -0,0 +1,378 @@ +package query + +import ( + "context" + "database/sql" + _ "embed" + errs "errors" + "fmt" + "strings" + "time" + + sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/call" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/query/projection" + "github.com/zitadel/zitadel/internal/telemetry/tracing" +) + +type State struct { + LastRun time.Time + + Position float64 + EventCreatedAt time.Time + AggregateID string + AggregateType eventstore.AggregateType + Sequence uint64 +} + +type CurrentStates struct { + SearchResponse + CurrentStates []*CurrentState +} + +type CurrentState struct { + ProjectionName string + State +} + +type CurrentStateSearchQueries struct { + SearchRequest + Queries []SearchQuery +} + +func NewCurrentStatesInstanceIDSearchQuery(instanceID string) (SearchQuery, error) { + return NewTextQuery(CurrentStateColInstanceID, instanceID, TextEquals) +} + +func NewCurrentStatesProjectionSearchQuery(projection string) (SearchQuery, error) { + return NewTextQuery(CurrentStateColProjectionName, projection, TextEquals) +} + +func (q *CurrentStateSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { + query = q.SearchRequest.toQuery(query) + for _, q := range q.Queries { + query = q.toQuery(query) + } + return query +} + +func (q *Queries) SearchCurrentStates(ctx context.Context, queries *CurrentStateSearchQueries) (currentStates *CurrentStates, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + query, scan := prepareCurrentStateQuery(ctx, q.client) + stmt, args, err := queries.toQuery(query).ToSql() + if err != nil { + return nil, errors.ThrowInvalidArgument(err, "QUERY-MmFef", "Errors.Query.InvalidRequest") + } + + err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { + currentStates, err = scan(rows) + return err + }, stmt, args...) + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-22H8f", "Errors.Internal") + } + + return currentStates, nil +} + +func (q *Queries) latestState(ctx context.Context, projections ...table) (state *State, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + query, scan := prepareLatestState(ctx, q.client) + or := make(sq.Or, len(projections)) + for i, projection := range projections { + or[i] = sq.Eq{CurrentStateColProjectionName.identifier(): projection.name} + } + stmt, args, err := query. + Where(or). + Where(sq.Eq{CurrentStateColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}). + OrderBy(CurrentStateColEventDate.identifier() + " DESC"). + ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement") + } + + err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { + state, err = scan(row) + return err + }, stmt, args...) + + return state, err +} + +func (q *Queries) ClearCurrentSequence(ctx context.Context, projectionName string) (err error) { + tx, err := q.client.Begin() + if err != nil { + return errors.ThrowInternal(err, "QUERY-9iOpr", "Errors.RemoveFailed") + } + defer func() { + if err != nil { + rollbackErr := tx.Rollback() + logging.OnError(rollbackErr).Debug("rollback failed") + return + } + if commitErr := tx.Commit(); commitErr != nil { + err = errors.ThrowInternal(commitErr, "QUERY-JGD0l", "Errors.Internal") + } + }() + + name, err := q.checkAndLock(tx, projectionName) + if err != nil { + return err + } + + tables, err := tablesForReset(ctx, tx, name) + if err != nil { + return err + } + err = reset(ctx, tx, tables, name) + if err != nil { + return err + } + err = tx.Commit() + if err != nil { + return errors.ThrowInternal(err, "QUERY-Sfvsc", "Errors.Internal") + } + return nil +} + +func (q *Queries) checkAndLock(tx *sql.Tx, projectionName string) (name string, err error) { + stmt, args, err := sq.Select(CurrentStateColProjectionName.identifier()). + From(currentStateTable.identifier()). + Where(sq.Eq{ + CurrentStateColProjectionName.identifier(): projectionName, + }).Suffix("FOR UPDATE"). + PlaceholderFormat(sq.Dollar). + ToSql() + if err != nil { + return "", errors.ThrowInternal(err, "QUERY-UJTUy", "Errors.Internal") + } + row := tx.QueryRow(stmt, args...) + if err := row.Scan(&name); err != nil || name == "" { + return "", errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") + } + return name, nil +} + +func tablesForReset(ctx context.Context, tx *sql.Tx, projectionName string) (tables []string, err error) { + names := strings.Split(projectionName, ".") + if len(names) != 2 { + return nil, errors.ThrowInvalidArgument(nil, "QUERY-wk1jr", "Errors.InvalidArgument") + } + schema := names[0] + tablePrefix := names[1] + + tablesQuery, args, err := sq.Select("table_name"). + From("[show tables from " + schema + "]"). + Where( + sq.And{ + sq.Eq{"type": "table"}, + sq.NotEq{"table_name": []string{"locks", "current_sequences", "current_states", "failed_events", "failed_events2"}}, + sq.Like{"table_name": tablePrefix + "%"}, + }). + PlaceholderFormat(sq.Dollar). + ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-ASff2", "Errors.ProjectionName.Invalid") + } + + rows, err := tx.QueryContext(ctx, tablesQuery, args...) + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-Dgfw", "Errors.ProjectionName.Invalid") + } + defer rows.Close() + + for rows.Next() { + var tableName string + if err := rows.Scan(&tableName); err != nil { + return nil, errors.ThrowInternal(err, "QUERY-ej8fn", "Errors.ProjectionName.Invalid") + } + tables = append(tables, schema+"."+tableName) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return tables, nil +} + +func reset(ctx context.Context, tx *sql.Tx, tables []string, projectionName string) error { + for _, tableName := range tables { + _, err := tx.Exec(fmt.Sprintf("TRUNCATE %s cascade", tableName)) + if err != nil { + return errors.ThrowInternal(err, "QUERY-3n92f", "Errors.RemoveFailed") + } + } + update, args, err := sq.Update(currentStateTable.identifier()). + Set(CurrentStateColEventDate.name, 0). + Where(sq.Eq{ + CurrentStateColProjectionName.name: projectionName, + }). + PlaceholderFormat(sq.Dollar). + ToSql() + if err != nil { + return errors.ThrowInternal(err, "QUERY-Ff3tw", "Errors.RemoveFailed") + } + _, err = tx.Exec(update, args...) + if err != nil { + return errors.ThrowInternal(err, "QUERY-NFiws", "Errors.RemoveFailed") + } + return nil +} + +func prepareLatestState(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*State, error)) { + return sq.Select( + CurrentStateColEventDate.identifier(), + CurrentStateColPosition.identifier(), + CurrentStateColLastUpdated.identifier()). + From(currentStateTable.identifier() + db.Timetravel(call.Took(ctx))). + PlaceholderFormat(sq.Dollar), + func(row *sql.Row) (*State, error) { + var ( + creationDate sql.NullTime + lastUpdated sql.NullTime + position sql.NullFloat64 + ) + err := row.Scan( + &creationDate, + &position, + &lastUpdated, + ) + if err != nil && !errs.Is(err, sql.ErrNoRows) { + return nil, errors.ThrowInternal(err, "QUERY-aAZ1D", "Errors.Internal") + } + return &State{ + EventCreatedAt: creationDate.Time, + LastRun: lastUpdated.Time, + Position: position.Float64, + }, nil + } +} + +func prepareCurrentStateQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*CurrentStates, error)) { + return sq.Select( + CurrentStateColLastUpdated.identifier(), + CurrentStateColEventDate.identifier(), + CurrentStateColPosition.identifier(), + CurrentStateColProjectionName.identifier(), + CurrentStateColAggregateType.identifier(), + CurrentStateColAggregateID.identifier(), + CurrentStateColSequence.identifier(), + countColumn.identifier()). + From(currentStateTable.identifier() + db.Timetravel(call.Took(ctx))). + PlaceholderFormat(sq.Dollar), + func(rows *sql.Rows) (*CurrentStates, error) { + states := make([]*CurrentState, 0) + var count uint64 + for rows.Next() { + currentState := new(CurrentState) + var ( + lastRun sql.NullTime + eventDate sql.NullTime + currentPosition sql.NullFloat64 + aggregateType sql.NullString + aggregateID sql.NullString + sequence sql.NullInt64 + ) + + err := rows.Scan( + &lastRun, + &eventDate, + ¤tPosition, + ¤tState.ProjectionName, + &aggregateType, + &aggregateID, + &sequence, + &count, + ) + if err != nil { + return nil, err + } + currentState.State.EventCreatedAt = eventDate.Time + currentState.State.LastRun = lastRun.Time + currentState.Position = currentPosition.Float64 + currentState.AggregateType = eventstore.AggregateType(aggregateType.String) + currentState.AggregateID = aggregateID.String + currentState.Sequence = uint64(sequence.Int64) + states = append(states, currentState) + } + + if err := rows.Close(); err != nil { + return nil, errors.ThrowInternal(err, "QUERY-jbJ77", "Errors.Query.CloseRows") + } + + return &CurrentStates{ + CurrentStates: states, + SearchResponse: SearchResponse{ + Count: count, + }, + }, nil + } +} + +var ( + currentStateTable = table{ + name: projection.CurrentStateTable, + instanceIDCol: "instance_id", + } + CurrentStateColEventDate = Column{ + name: "event_date", + table: currentStateTable, + } + CurrentStateColPosition = Column{ + name: "position", + table: currentStateTable, + } + CurrentStateColAggregateType = Column{ + name: "aggregate_type", + table: currentStateTable, + } + CurrentStateColAggregateID = Column{ + name: "aggregate_id", + table: currentStateTable, + } + CurrentStateColSequence = Column{ + name: "sequence", + table: currentStateTable, + } + CurrentStateColLastUpdated = Column{ + name: "last_updated", + table: currentStateTable, + } + CurrentStateColProjectionName = Column{ + name: "projection_name", + table: currentStateTable, + } + CurrentStateColInstanceID = Column{ + name: "instance_id", + table: currentStateTable, + } +) + +var ( + locksTable = table{ + name: projection.LocksTable, + instanceIDCol: "instance_id", + } + LocksColLockerID = Column{ + name: "locker_id", + table: locksTable, + } + LocksColUntil = Column{ + name: "locked_until", + table: locksTable, + } + LocksColProjectionName = Column{ + name: "projection_name", + table: locksTable, + } +) diff --git a/internal/query/current_sequence_test.go b/internal/query/current_state_test.go similarity index 51% rename from internal/query/current_sequence_test.go rename to internal/query/current_state_test.go index 20df6aea0..c76dae710 100644 --- a/internal/query/current_sequence_test.go +++ b/internal/query/current_state_test.go @@ -10,18 +10,26 @@ import ( ) var ( - currentSequenceStmt = `SELECT max(projections.current_sequences.current_sequence) as current_sequence,` + - ` max(projections.current_sequences.timestamp) as timestamp,` + - ` projections.current_sequences.projection_name,` + + currentSequenceStmt = `SELECT` + + ` projections.current_states.last_updated,` + + ` projections.current_states.event_date,` + + ` projections.current_states.position,` + + ` projections.current_states.projection_name,` + + ` projections.current_states.aggregate_type,` + + ` projections.current_states.aggregate_id,` + + ` projections.current_states.sequence,` + ` COUNT(*) OVER ()` + - ` FROM projections.current_sequences` + - " AS OF SYSTEM TIME '-1 ms' " + - ` GROUP BY projections.current_sequences.projection_name` + ` FROM projections.current_states` + + " AS OF SYSTEM TIME '-1 ms' " currentSequenceCols = []string{ - "current_sequence", - "timestamp", + "last_updated", + "event_date", + "position", "projection_name", + "aggregate_type", + "aggregate_id", + "event_sequence", "count", } ) @@ -39,7 +47,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) { }{ { name: "prepareCurrentSequencesQuery no result", - prepare: prepareCurrentSequencesQuery, + prepare: prepareCurrentStateQuery, want: want{ sqlExpectations: mockQueries( regexp.QuoteMeta(currentSequenceStmt), @@ -47,79 +55,109 @@ func Test_CurrentSequencesPrepares(t *testing.T) { nil, ), }, - object: &CurrentSequences{CurrentSequences: []*CurrentSequence{}}, + object: &CurrentStates{CurrentStates: []*CurrentState{}}, }, { name: "prepareCurrentSequencesQuery one result", - prepare: prepareCurrentSequencesQuery, + prepare: prepareCurrentStateQuery, want: want{ sqlExpectations: mockQueries( regexp.QuoteMeta(currentSequenceStmt), currentSequenceCols, [][]driver.Value{ { - uint64(20211108), testNow, + testNow, + float64(20211108), "projection-name", + "agg-type", + "agg-id", + uint64(20211108), }, }, ), }, - object: &CurrentSequences{ + object: &CurrentStates{ SearchResponse: SearchResponse{ Count: 1, }, - CurrentSequences: []*CurrentSequence{ + CurrentStates: []*CurrentState{ { - Timestamp: testNow, - CurrentSequence: 20211108, - ProjectionName: "projection-name", + ProjectionName: "projection-name", + State: State{ + EventCreatedAt: testNow, + LastRun: testNow, + Position: 20211108, + AggregateID: "agg-id", + AggregateType: "agg-type", + Sequence: 20211108, + }, }, }, }, }, { name: "prepareCurrentSequencesQuery multiple result", - prepare: prepareCurrentSequencesQuery, + prepare: prepareCurrentStateQuery, want: want{ sqlExpectations: mockQueries( regexp.QuoteMeta(currentSequenceStmt), currentSequenceCols, [][]driver.Value{ { - uint64(20211108), testNow, + testNow, + float64(20211108), "projection-name", + "agg-type", + "agg-id", + uint64(20211108), }, { - uint64(20211108), testNow, - "projection-name-2", + testNow, + float64(20211108), + "projection-name2", + "agg-type", + "agg-id", + uint64(20211108), }, }, ), }, - object: &CurrentSequences{ + object: &CurrentStates{ SearchResponse: SearchResponse{ Count: 2, }, - CurrentSequences: []*CurrentSequence{ + CurrentStates: []*CurrentState{ { - Timestamp: testNow, - CurrentSequence: 20211108, - ProjectionName: "projection-name", + ProjectionName: "projection-name", + State: State{ + EventCreatedAt: testNow, + Position: 20211108, + LastRun: testNow, + AggregateID: "agg-id", + AggregateType: "agg-type", + Sequence: 20211108, + }, }, { - Timestamp: testNow, - CurrentSequence: 20211108, - ProjectionName: "projection-name-2", + ProjectionName: "projection-name2", + State: State{ + EventCreatedAt: testNow, + Position: 20211108, + LastRun: testNow, + AggregateID: "agg-id", + AggregateType: "agg-type", + Sequence: 20211108, + }, }, }, }, }, { name: "prepareCurrentSequencesQuery sql err", - prepare: prepareCurrentSequencesQuery, + prepare: prepareCurrentStateQuery, want: want{ sqlExpectations: mockQueryErr( regexp.QuoteMeta(currentSequenceStmt), @@ -132,7 +170,7 @@ func Test_CurrentSequencesPrepares(t *testing.T) { return nil, true }, }, - object: (*CurrentSequences)(nil), + object: (*CurrentStates)(nil), }, } for _, tt := range tests { diff --git a/internal/query/custom_text.go b/internal/query/custom_text.go index 5f8386680..794271f3e 100644 --- a/internal/query/custom_text.go +++ b/internal/query/custom_text.go @@ -112,7 +112,7 @@ func (q *Queries) CustomTextList(ctx context.Context, aggregateID, template, lan return nil, errors.ThrowInternal(err, "QUERY-2j00f", "Errors.Internal") } - texts.LatestSequence, err = q.latestSequence(ctx, projectsTable) + texts.State, err = q.latestState(ctx, projectsTable) return texts, err } @@ -142,7 +142,7 @@ func (q *Queries) CustomTextListByTemplate(ctx context.Context, aggregateID, tem return nil, errors.ThrowInternal(err, "QUERY-3n9ge", "Errors.Internal") } - texts.LatestSequence, err = q.latestSequence(ctx, projectsTable) + texts.State, err = q.latestState(ctx, projectsTable) return texts, err } diff --git a/internal/query/device_auth.go b/internal/query/device_auth.go index 91d26ba6c..c16fcdf58 100644 --- a/internal/query/device_auth.go +++ b/internal/query/device_auth.go @@ -126,7 +126,7 @@ func prepareDeviceAuthQuery(ctx context.Context, db prepareDatabase) (sq.SelectB return sq.Select(deviceAuthSelectColumns...).From(deviceAuthTable.identifier()).PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*domain.DeviceAuth, error) { dst := new(domain.DeviceAuth) - var scopes database.StringArray + var scopes database.TextArray[string] err := row.Scan( &dst.AggregateID, diff --git a/internal/query/device_auth_test.go b/internal/query/device_auth_test.go index 112032d32..a9f5efcf2 100644 --- a/internal/query/device_auth_test.go +++ b/internal/query/device_auth_test.go @@ -43,7 +43,7 @@ var ( expectedDeviceAuthValues = []driver.Value{ "primary-id", "client-id", - database.StringArray{"a", "b", "c"}, + database.TextArray[string]{"a", "b", "c"}, testNow, domain.DeviceAuthStateApproved, "subject", diff --git a/internal/query/domain_policy.go b/internal/query/domain_policy.go index 77e821d39..59e75b9aa 100644 --- a/internal/query/domain_policy.go +++ b/internal/query/domain_policy.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -91,7 +94,8 @@ func (q *Queries) DomainPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.DomainPolicyProjection.Trigger(ctx) + ctx, err = projection.DomainPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.And{ sq.Eq{DomainPolicyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}, diff --git a/internal/query/event.go b/internal/query/event.go index b54322882..63ffa3fe4 100644 --- a/internal/query/event.go +++ b/internal/query/event.go @@ -11,7 +11,7 @@ import ( type Event struct { Editor *EventEditor - Aggregate eventstore.Aggregate + Aggregate *eventstore.Aggregate Sequence uint64 CreationDate time.Time Type string @@ -48,7 +48,7 @@ func filterAuditLogRetention(ctx context.Context, events []eventstore.Event, aud } filteredEvents := make([]eventstore.Event, 0, len(events)) for _, event := range events { - if event.CreationDate().After(callTime.Add(-auditLogRetention)) { + if event.CreatedAt().After(callTime.Add(-auditLogRetention)) { filteredEvents = append(filteredEvents, event) } } @@ -77,23 +77,23 @@ func (q *Queries) convertEvent(ctx context.Context, event eventstore.Event, user var err error defer func() { span.EndWithError(err) }() - editor, ok := users[event.EditorUser()] + editor, ok := users[event.Creator()] if !ok { - editor = q.editorUserByID(ctx, event.EditorUser()) - users[event.EditorUser()] = editor + editor = q.editorUserByID(ctx, event.Creator()) + users[event.Creator()] = editor } return &Event{ Editor: &EventEditor{ - ID: event.EditorUser(), - Service: event.EditorService(), + ID: event.Creator(), + Service: "zitadel", DisplayName: editor.DisplayName, PreferedLoginName: editor.PreferedLoginName, AvatarKey: editor.AvatarKey, }, Aggregate: event.Aggregate(), Sequence: event.Sequence(), - CreationDate: event.CreationDate(), + CreationDate: event.CreatedAt(), Type: string(event.Type()), Payload: event.DataAsBytes(), } diff --git a/internal/query/failed_events.go b/internal/query/failed_events.go index ed335bf62..a1c2b31e4 100644 --- a/internal/query/failed_events.go +++ b/internal/query/failed_events.go @@ -15,6 +15,8 @@ import ( const ( failedEventsColumnProjectionName = "projection_name" failedEventsColumnFailedSequence = "failed_sequence" + failedEventsColumnAggregateType = "aggregate_type" + failedEventsColumnAggregateID = "aggregate_id" failedEventsColumnFailureCount = "failure_count" failedEventsColumnLastFailed = "last_failed" failedEventsColumnError = "error" @@ -34,6 +36,14 @@ var ( name: failedEventsColumnFailedSequence, table: failedEventsTable, } + FailedeventsColumnAggregateType = Column{ + name: failedEventsColumnAggregateType, + table: failedEventsTable, + } + FailedeventsColumnAggregateID = Column{ + name: failedEventsColumnAggregateID, + table: failedEventsTable, + } FailedEventsColumnFailureCount = Column{ name: failedEventsColumnFailureCount, table: failedEventsTable, @@ -59,6 +69,8 @@ type FailedEvents struct { type FailedEvent struct { ProjectionName string + AggregateType string + AggregateID string FailedSequence uint64 FailureCount uint64 Error string @@ -131,6 +143,8 @@ func prepareFailedEventsQuery(ctx context.Context, db prepareDatabase) (sq.Selec return sq.Select( FailedEventsColumnProjectionName.identifier(), FailedEventsColumnFailedSequence.identifier(), + FailedeventsColumnAggregateType.identifier(), + FailedeventsColumnAggregateID.identifier(), FailedEventsColumnFailureCount.identifier(), FailedEventsColumnLastFailed.identifier(), FailedEventsColumnError.identifier(), @@ -146,6 +160,8 @@ func prepareFailedEventsQuery(ctx context.Context, db prepareDatabase) (sq.Selec err := rows.Scan( &failedEvent.ProjectionName, &failedEvent.FailedSequence, + &failedEvent.AggregateType, + &failedEvent.AggregateID, &failedEvent.FailureCount, &lastFailed, &failedEvent.Error, diff --git a/internal/query/failed_events_test.go b/internal/query/failed_events_test.go index 01ba2bee2..7e575b589 100644 --- a/internal/query/failed_events_test.go +++ b/internal/query/failed_events_test.go @@ -10,18 +10,23 @@ import ( ) var ( - prepareFailedEventsStmt = `SELECT projections.failed_events.projection_name,` + - ` projections.failed_events.failed_sequence,` + - ` projections.failed_events.failure_count,` + - ` projections.failed_events.last_failed,` + - ` projections.failed_events.error,` + + prepareFailedEventsStmt = `SELECT` + + ` projections.failed_events2.projection_name,` + + ` projections.failed_events2.failed_sequence,` + + ` projections.failed_events2.aggregate_type,` + + ` projections.failed_events2.aggregate_id,` + + ` projections.failed_events2.failure_count,` + + ` projections.failed_events2.last_failed,` + + ` projections.failed_events2.error,` + ` COUNT(*) OVER ()` + - ` FROM projections.failed_events` + + ` FROM projections.failed_events2` + ` AS OF SYSTEM TIME '-1 ms'` prepareFailedEventsCols = []string{ "projection_name", "failed_sequence", + "aggregate_type", + "aggregate_id", "failure_count", "last_failed", "error", @@ -63,6 +68,8 @@ func Test_FailedEventsPrepares(t *testing.T) { { "projection-name", uint64(20211108), + "agg-type", + "agg-id", uint64(2), testNow, "error", @@ -81,6 +88,8 @@ func Test_FailedEventsPrepares(t *testing.T) { FailureCount: 2, LastFailed: testNow, Error: "error", + AggregateType: "agg-type", + AggregateID: "agg-id", }, }, }, @@ -96,6 +105,8 @@ func Test_FailedEventsPrepares(t *testing.T) { { "projection-name", uint64(20211108), + "agg-type", + "agg-id", 2, testNow, "error", @@ -103,6 +114,8 @@ func Test_FailedEventsPrepares(t *testing.T) { { "projection-name-2", uint64(20211108), + "agg-type", + "agg-id", 2, nil, "error", @@ -121,12 +134,16 @@ func Test_FailedEventsPrepares(t *testing.T) { FailureCount: 2, LastFailed: testNow, Error: "error", + AggregateType: "agg-type", + AggregateID: "agg-id", }, { ProjectionName: "projection-name-2", FailedSequence: 20211108, FailureCount: 2, Error: "error", + AggregateType: "agg-type", + AggregateID: "agg-id", }, }, }, diff --git a/internal/query/iam_member.go b/internal/query/iam_member.go index bba45fbf5..9b6018191 100644 --- a/internal/query/iam_member.go +++ b/internal/query/iam_member.go @@ -91,7 +91,7 @@ func (q *Queries) IAMMembers(ctx context.Context, queries *IAMMembersQuery, with return nil, errors.ThrowInvalidArgument(err, "QUERY-USNwM", "Errors.Query.InvalidRequest") } - currentSequence, err := q.latestSequence(ctx, instanceMemberTable) + currentSequence, err := q.latestState(ctx, instanceMemberTable) if err != nil { return nil, err } @@ -103,7 +103,7 @@ func (q *Queries) IAMMembers(ctx context.Context, queries *IAMMembersQuery, with if err != nil { return nil, errors.ThrowInternal(err, "QUERY-Pdg1I", "Errors.Internal") } - members.LatestSequence = currentSequence + members.State = currentSequence return members, err } diff --git a/internal/query/iam_member_test.go b/internal/query/iam_member_test.go index ed2bd6cd3..68e1f90f7 100644 --- a/internal/query/iam_member_test.go +++ b/internal/query/iam_member_test.go @@ -98,7 +98,7 @@ func Test_IAMMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -122,7 +122,7 @@ func Test_IAMMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -148,7 +148,7 @@ func Test_IAMMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -172,7 +172,7 @@ func Test_IAMMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", @@ -198,7 +198,7 @@ func Test_IAMMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-1", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -214,7 +214,7 @@ func Test_IAMMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-2", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -238,7 +238,7 @@ func Test_IAMMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-1", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -253,7 +253,7 @@ func Test_IAMMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-2", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", diff --git a/internal/query/idp.go b/internal/query/idp.go index e29077b0b..90783c45d 100644 --- a/internal/query/idp.go +++ b/internal/query/idp.go @@ -8,12 +8,15 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -43,7 +46,7 @@ type OIDCIDP struct { ClientID string ClientSecret *crypto.CryptoValue Issuer string - Scopes database.StringArray + Scopes database.TextArray[string] DisplayNameMapping domain.OIDCMappingField UsernameMapping domain.OIDCMappingField AuthorizationEndpoint string @@ -193,7 +196,8 @@ func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, shouldTriggerBulk defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.IDPProjection.Trigger(ctx) + ctx, err = projection.IDPProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{ @@ -247,7 +251,7 @@ func (q *Queries) IDPs(ctx context.Context, queries *IDPSearchQueries, withOwner if err != nil { return nil, errors.ThrowInternal(err, "QUERY-xPlVH", "Errors.Internal") } - idps.LatestSequence, err = q.latestSequence(ctx, idpTable) + idps.State, err = q.latestState(ctx, idpTable) return idps, err } @@ -325,7 +329,7 @@ func prepareIDPByIDQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil oidcClientID := sql.NullString{} oidcClientSecret := new(crypto.CryptoValue) oidcIssuer := sql.NullString{} - oidcScopes := database.StringArray{} + oidcScopes := database.TextArray[string]{} oidcDisplayNameMapping := sql.NullInt32{} oidcUsernameMapping := sql.NullInt32{} oidcAuthorizationEndpoint := sql.NullString{} @@ -437,7 +441,7 @@ func prepareIDPsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder oidcClientID := sql.NullString{} oidcClientSecret := new(crypto.CryptoValue) oidcIssuer := sql.NullString{} - oidcScopes := database.StringArray{} + oidcScopes := database.TextArray[string]{} oidcDisplayNameMapping := sql.NullInt32{} oidcUsernameMapping := sql.NullInt32{} oidcAuthorizationEndpoint := sql.NullString{} diff --git a/internal/query/idp_login_policy_link.go b/internal/query/idp_login_policy_link.go index 89ee4a08b..7bc60bf17 100644 --- a/internal/query/idp_login_policy_link.go +++ b/internal/query/idp_login_policy_link.go @@ -113,7 +113,7 @@ func (q *Queries) IDPLoginPolicyLinks(ctx context.Context, resourceOwner string, if err != nil { return nil, errors.ThrowInternal(err, "QUERY-ZkKUc", "Errors.Internal") } - idps.LatestSequence, err = q.latestSequence(ctx, idpLoginPolicyLinkTable) + idps.State, err = q.latestState(ctx, idpLoginPolicyLinkTable) return idps, err } diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index b12824dbb..e8abb0d7a 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -8,12 +8,15 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/telemetry/tracing" @@ -59,7 +62,7 @@ type OAuthIDPTemplate struct { AuthorizationEndpoint string TokenEndpoint string UserEndpoint string - Scopes database.StringArray + Scopes database.TextArray[string] IDAttribute string } @@ -68,7 +71,7 @@ type OIDCIDPTemplate struct { ClientID string ClientSecret *crypto.CryptoValue Issuer string - Scopes database.StringArray + Scopes database.TextArray[string] IsIDTokenMapping bool } @@ -84,7 +87,7 @@ type AzureADIDPTemplate struct { IDPID string ClientID string ClientSecret *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] Tenant string IsEmailVerified bool } @@ -93,7 +96,7 @@ type GitHubIDPTemplate struct { IDPID string ClientID string ClientSecret *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] } type GitHubEnterpriseIDPTemplate struct { @@ -103,14 +106,14 @@ type GitHubEnterpriseIDPTemplate struct { AuthorizationEndpoint string TokenEndpoint string UserEndpoint string - Scopes database.StringArray + Scopes database.TextArray[string] } type GitLabIDPTemplate struct { IDPID string ClientID string ClientSecret *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] } type GitLabSelfHostedIDPTemplate struct { @@ -118,14 +121,14 @@ type GitLabSelfHostedIDPTemplate struct { Issuer string ClientID string ClientSecret *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] } type GoogleIDPTemplate struct { IDPID string ClientID string ClientSecret *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] } type LDAPIDPTemplate struct { @@ -148,7 +151,7 @@ type AppleIDPTemplate struct { TeamID string KeyID string PrivateKey *crypto.CryptoValue - Scopes database.StringArray + Scopes database.TextArray[string] } type SAMLIDPTemplate struct { @@ -701,7 +704,8 @@ func (q *Queries) IDPTemplateByID(ctx context.Context, shouldTriggerBulk bool, i defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.IDPTemplateProjection.Trigger(ctx) + ctx, err = projection.IDPTemplateProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("unable to trigger") } eq := sq.Eq{ @@ -751,7 +755,7 @@ func (q *Queries) IDPTemplates(ctx context.Context, queries *IDPTemplateSearchQu if err != nil { return nil, errors.ThrowInternal(err, "QUERY-BDFrq", "Errors.Internal") } - idps.LatestSequence, err = q.latestSequence(ctx, idpTemplateTable) + idps.State, err = q.latestState(ctx, idpTemplateTable) return idps, err } @@ -928,14 +932,14 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se oauthAuthorizationEndpoint := sql.NullString{} oauthTokenEndpoint := sql.NullString{} oauthUserEndpoint := sql.NullString{} - oauthScopes := database.StringArray{} + oauthScopes := database.TextArray[string]{} oauthIDAttribute := sql.NullString{} oidcID := sql.NullString{} oidcIssuer := sql.NullString{} oidcClientID := sql.NullString{} oidcClientSecret := new(crypto.CryptoValue) - oidcScopes := database.StringArray{} + oidcScopes := database.TextArray[string]{} oidcIDTokenMapping := sql.NullBool{} jwtID := sql.NullString{} @@ -947,14 +951,14 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se azureadID := sql.NullString{} azureadClientID := sql.NullString{} azureadClientSecret := new(crypto.CryptoValue) - azureadScopes := database.StringArray{} + azureadScopes := database.TextArray[string]{} azureadTenant := sql.NullString{} azureadIsEmailVerified := sql.NullBool{} githubID := sql.NullString{} githubClientID := sql.NullString{} githubClientSecret := new(crypto.CryptoValue) - githubScopes := database.StringArray{} + githubScopes := database.TextArray[string]{} githubEnterpriseID := sql.NullString{} githubEnterpriseClientID := sql.NullString{} @@ -962,23 +966,23 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se githubEnterpriseAuthorizationEndpoint := sql.NullString{} githubEnterpriseTokenEndpoint := sql.NullString{} githubEnterpriseUserEndpoint := sql.NullString{} - githubEnterpriseScopes := database.StringArray{} + githubEnterpriseScopes := database.TextArray[string]{} gitlabID := sql.NullString{} gitlabClientID := sql.NullString{} gitlabClientSecret := new(crypto.CryptoValue) - gitlabScopes := database.StringArray{} + gitlabScopes := database.TextArray[string]{} gitlabSelfHostedID := sql.NullString{} gitlabSelfHostedIssuer := sql.NullString{} gitlabSelfHostedClientID := sql.NullString{} gitlabSelfHostedClientSecret := new(crypto.CryptoValue) - gitlabSelfHostedScopes := database.StringArray{} + gitlabSelfHostedScopes := database.TextArray[string]{} googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) - googleScopes := database.StringArray{} + googleScopes := database.TextArray[string]{} samlID := sql.NullString{} var samlMetadata []byte @@ -988,14 +992,14 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se samlWithSignedRequest := sql.NullBool{} ldapID := sql.NullString{} - ldapServers := database.StringArray{} + ldapServers := database.TextArray[string]{} ldapStartTls := sql.NullBool{} ldapBaseDN := sql.NullString{} ldapBindDN := sql.NullString{} ldapBindPassword := new(crypto.CryptoValue) ldapUserBase := sql.NullString{} - ldapUserObjectClasses := database.StringArray{} - ldapUserFilters := database.StringArray{} + ldapUserObjectClasses := database.TextArray[string]{} + ldapUserFilters := database.TextArray[string]{} ldapTimeout := sql.NullInt64{} ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} @@ -1016,7 +1020,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se appleTeamID := sql.NullString{} appleKeyID := sql.NullString{} applePrivateKey := new(crypto.CryptoValue) - appleScopes := database.StringArray{} + appleScopes := database.TextArray[string]{} err := row.Scan( &idpTemplate.ID, @@ -1418,14 +1422,14 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec oauthAuthorizationEndpoint := sql.NullString{} oauthTokenEndpoint := sql.NullString{} oauthUserEndpoint := sql.NullString{} - oauthScopes := database.StringArray{} + oauthScopes := database.TextArray[string]{} oauthIDAttribute := sql.NullString{} oidcID := sql.NullString{} oidcIssuer := sql.NullString{} oidcClientID := sql.NullString{} oidcClientSecret := new(crypto.CryptoValue) - oidcScopes := database.StringArray{} + oidcScopes := database.TextArray[string]{} oidcIDTokenMapping := sql.NullBool{} jwtID := sql.NullString{} @@ -1437,14 +1441,14 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec azureadID := sql.NullString{} azureadClientID := sql.NullString{} azureadClientSecret := new(crypto.CryptoValue) - azureadScopes := database.StringArray{} + azureadScopes := database.TextArray[string]{} azureadTenant := sql.NullString{} azureadIsEmailVerified := sql.NullBool{} githubID := sql.NullString{} githubClientID := sql.NullString{} githubClientSecret := new(crypto.CryptoValue) - githubScopes := database.StringArray{} + githubScopes := database.TextArray[string]{} githubEnterpriseID := sql.NullString{} githubEnterpriseClientID := sql.NullString{} @@ -1452,23 +1456,23 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec githubEnterpriseAuthorizationEndpoint := sql.NullString{} githubEnterpriseTokenEndpoint := sql.NullString{} githubEnterpriseUserEndpoint := sql.NullString{} - githubEnterpriseScopes := database.StringArray{} + githubEnterpriseScopes := database.TextArray[string]{} gitlabID := sql.NullString{} gitlabClientID := sql.NullString{} gitlabClientSecret := new(crypto.CryptoValue) - gitlabScopes := database.StringArray{} + gitlabScopes := database.TextArray[string]{} gitlabSelfHostedID := sql.NullString{} gitlabSelfHostedIssuer := sql.NullString{} gitlabSelfHostedClientID := sql.NullString{} gitlabSelfHostedClientSecret := new(crypto.CryptoValue) - gitlabSelfHostedScopes := database.StringArray{} + gitlabSelfHostedScopes := database.TextArray[string]{} googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) - googleScopes := database.StringArray{} + googleScopes := database.TextArray[string]{} samlID := sql.NullString{} var samlMetadata []byte @@ -1478,14 +1482,14 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec samlWithSignedRequest := sql.NullBool{} ldapID := sql.NullString{} - ldapServers := database.StringArray{} + ldapServers := database.TextArray[string]{} ldapStartTls := sql.NullBool{} ldapBaseDN := sql.NullString{} ldapBindDN := sql.NullString{} ldapBindPassword := new(crypto.CryptoValue) ldapUserBase := sql.NullString{} - ldapUserObjectClasses := database.StringArray{} - ldapUserFilters := database.StringArray{} + ldapUserObjectClasses := database.TextArray[string]{} + ldapUserFilters := database.TextArray[string]{} ldapTimeout := sql.NullInt64{} ldapIDAttribute := sql.NullString{} ldapFirstNameAttribute := sql.NullString{} @@ -1506,7 +1510,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec appleTeamID := sql.NullString{} appleKeyID := sql.NullString{} applePrivateKey := new(crypto.CryptoValue) - appleScopes := database.StringArray{} + appleScopes := database.TextArray[string]{} err := rows.Scan( &idpTemplate.ID, diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index b6eb54aad..626e411c8 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -545,7 +545,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "authorization", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "id-attribute", // oidc nil, @@ -698,7 +698,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "issuer", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, // jwt nil, @@ -1004,7 +1004,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "idp-id", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, // github enterprise nil, nil, @@ -1159,7 +1159,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "idp-id", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, // gitlab self hosted nil, nil, @@ -1307,7 +1307,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "issuer", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, // google nil, nil, @@ -1455,7 +1455,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "idp-id", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, // saml nil, nil, @@ -1751,14 +1751,14 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, // ldap config "idp-id", - database.StringArray{"server"}, + database.TextArray[string]{"server"}, true, "base", "dn", nil, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -1941,7 +1941,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "team_id", "key_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, ), }, @@ -2229,14 +2229,14 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, // ldap config "idp-id", - database.StringArray{"server"}, + database.TextArray[string]{"server"}, true, "base", "dn", nil, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -2545,14 +2545,14 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, // ldap config "idp-id-ldap", - database.StringArray{"server"}, + database.TextArray[string]{"server"}, true, "base", "dn", nil, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -2757,7 +2757,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "idp-id-google", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, // saml nil, nil, @@ -2818,7 +2818,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "authorization", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "id-attribute", // oidc nil, @@ -2936,7 +2936,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "issuer", "client_id", nil, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, // jwt nil, diff --git a/internal/query/idp_test.go b/internal/query/idp_test.go index a44252369..5b34fac06 100644 --- a/internal/query/idp_test.go +++ b/internal/query/idp_test.go @@ -181,7 +181,7 @@ func Test_IDPPrepares(t *testing.T) { "oidc-client-id", nil, "oidc-issuer", - database.StringArray{"scope"}, + database.TextArray[string]{"scope"}, domain.OIDCMappingFieldEmail, domain.OIDCMappingFieldPreferredLoginName, "auth.endpoint.ch", @@ -211,7 +211,7 @@ func Test_IDPPrepares(t *testing.T) { ClientID: "oidc-client-id", ClientSecret: &crypto.CryptoValue{}, Issuer: "oidc-issuer", - Scopes: database.StringArray{"scope"}, + Scopes: database.TextArray[string]{"scope"}, DisplayNameMapping: domain.OIDCMappingFieldEmail, UsernameMapping: domain.OIDCMappingFieldPreferredLoginName, AuthorizationEndpoint: "auth.endpoint.ch", @@ -385,7 +385,7 @@ func Test_IDPPrepares(t *testing.T) { "oidc-client-id", nil, "oidc-issuer", - database.StringArray{"scope"}, + database.TextArray[string]{"scope"}, domain.OIDCMappingFieldEmail, domain.OIDCMappingFieldPreferredLoginName, "auth.endpoint.ch", @@ -421,7 +421,7 @@ func Test_IDPPrepares(t *testing.T) { ClientID: "oidc-client-id", ClientSecret: &crypto.CryptoValue{}, Issuer: "oidc-issuer", - Scopes: database.StringArray{"scope"}, + Scopes: database.TextArray[string]{"scope"}, DisplayNameMapping: domain.OIDCMappingFieldEmail, UsernameMapping: domain.OIDCMappingFieldPreferredLoginName, AuthorizationEndpoint: "auth.endpoint.ch", @@ -608,7 +608,7 @@ func Test_IDPPrepares(t *testing.T) { "oidc-client-id", nil, "oidc-issuer", - database.StringArray{"scope"}, + database.TextArray[string]{"scope"}, domain.OIDCMappingFieldEmail, domain.OIDCMappingFieldPreferredLoginName, "auth.endpoint.ch", @@ -684,7 +684,7 @@ func Test_IDPPrepares(t *testing.T) { ClientID: "oidc-client-id", ClientSecret: &crypto.CryptoValue{}, Issuer: "oidc-issuer", - Scopes: database.StringArray{"scope"}, + Scopes: database.TextArray[string]{"scope"}, DisplayNameMapping: domain.OIDCMappingFieldEmail, UsernameMapping: domain.OIDCMappingFieldPreferredLoginName, AuthorizationEndpoint: "auth.endpoint.ch", diff --git a/internal/query/idp_user_link.go b/internal/query/idp_user_link.go index 346ce129e..60d606604 100644 --- a/internal/query/idp_user_link.go +++ b/internal/query/idp_user_link.go @@ -110,7 +110,7 @@ func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQ if err != nil { return nil, errors.ThrowInternal(err, "QUERY-C1E4D", "Errors.Internal") } - idps.LatestSequence, err = q.latestSequence(ctx, idpUserLinkTable) + idps.State, err = q.latestState(ctx, idpUserLinkTable) return idps, err } diff --git a/internal/query/instance.go b/internal/query/instance.go index 77327e736..f570f0591 100644 --- a/internal/query/instance.go +++ b/internal/query/instance.go @@ -8,12 +8,14 @@ import ( "time" sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -88,7 +90,7 @@ type Instance struct { type csp struct { enabled bool - allowedOrigins database.StringArray + allowedOrigins database.TextArray[string] } type Instances struct { @@ -181,7 +183,8 @@ func (q *Queries) Instance(ctx context.Context, shouldTriggerBulk bool) (instanc defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.InstanceProjection.Trigger(ctx) + ctx, err = projection.InstanceProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := prepareInstanceDomainQuery(ctx, q.client, authz.GetInstance(ctx).RequestedDomain()) @@ -535,7 +538,7 @@ func prepareAuthzInstanceQuery(ctx context.Context, db prepareDatabase, host str instance.csp.enabled = securityPolicyEnabled.Bool } if instance.ID == "" { - return nil, errors.ThrowNotFound(nil, "QUERY-n0wng", "Errors.IAM.NotFound") + return nil, errors.ThrowNotFound(nil, "QUERY-1kIjX", "Errors.IAM.NotFound") } instance.DefaultLang = language.Make(lang) if err := rows.Close(); err != nil { diff --git a/internal/query/instance_domain.go b/internal/query/instance_domain.go index bb11e1aa8..ee5230f06 100644 --- a/internal/query/instance_domain.go +++ b/internal/query/instance_domain.go @@ -95,7 +95,7 @@ func (q *Queries) queryInstanceDomains(ctx context.Context, stmt string, scan fu if err != nil { return nil, err } - domains.LatestSequence, err = q.latestSequence(ctx, instanceDomainsTable) + domains.State, err = q.latestState(ctx, instanceDomainsTable) return domains, err } diff --git a/internal/query/key.go b/internal/query/key.go index 29a39fa06..ac00d0624 100644 --- a/internal/query/key.go +++ b/internal/query/key.go @@ -202,7 +202,7 @@ func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (keys *Publ return nil, errors.ThrowInternal(err, "QUERY-Sghn4", "Errors.Internal") } - keys.LatestSequence, err = q.latestSequence(ctx, keyTable) + keys.State, err = q.latestState(ctx, keyTable) if !errors.IsNotFound(err) { return keys, err } @@ -236,7 +236,7 @@ func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (key if err != nil { return nil, errors.ThrowInternal(err, "QUERY-WRFG4", "Errors.Internal") } - keys.LatestSequence, err = q.latestSequence(ctx, keyTable) + keys.State, err = q.latestState(ctx, keyTable) if !errors.IsNotFound(err) { return keys, err } diff --git a/internal/query/lockout_policy.go b/internal/query/lockout_policy.go index 7e7624dc2..e99316257 100644 --- a/internal/query/lockout_policy.go +++ b/internal/query/lockout_policy.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -86,7 +89,8 @@ func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.LockoutPolicyProjection.Trigger(ctx) + ctx, err = projection.LockoutPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{ LockoutColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), diff --git a/internal/query/login_policy.go b/internal/query/login_policy.go index 5d0a7e668..1968f2f06 100644 --- a/internal/query/login_policy.go +++ b/internal/query/login_policy.go @@ -8,11 +8,14 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -27,8 +30,8 @@ type LoginPolicy struct { AllowExternalIDPs bool ForceMFA bool ForceMFALocalOnly bool - SecondFactors database.EnumArray[domain.SecondFactorType] - MultiFactors database.EnumArray[domain.MultiFactorType] + SecondFactors database.Array[domain.SecondFactorType] + MultiFactors database.Array[domain.MultiFactorType] PasswordlessType domain.PasswordlessType IsDefault bool HidePasswordReset bool @@ -47,12 +50,12 @@ type LoginPolicy struct { type SecondFactors struct { SearchResponse - Factors database.EnumArray[domain.SecondFactorType] + Factors database.Array[domain.SecondFactorType] } type MultiFactors struct { SearchResponse - Factors database.EnumArray[domain.MultiFactorType] + Factors database.Array[domain.MultiFactorType] } var ( @@ -171,7 +174,8 @@ func (q *Queries) LoginPolicyByID(ctx context.Context, shouldTriggerBulk bool, o defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.LoginPolicyProjection.Trigger(ctx) + ctx, err = projection.LoginPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{LoginPolicyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} if !withOwnerRemoved { @@ -272,7 +276,7 @@ func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (factors if err != nil { return nil, err } - factors.LatestSequence, err = q.latestSequence(ctx, loginPolicyTable) + factors.State, err = q.latestState(ctx, loginPolicyTable) return factors, err } @@ -296,7 +300,7 @@ func (q *Queries) DefaultSecondFactors(ctx context.Context) (factors *SecondFact if err != nil { return nil, err } - factors.LatestSequence, err = q.latestSequence(ctx, loginPolicyTable) + factors.State, err = q.latestState(ctx, loginPolicyTable) return factors, err } @@ -332,7 +336,7 @@ func (q *Queries) MultiFactorsByOrg(ctx context.Context, orgID string) (factors if err != nil { return nil, err } - factors.LatestSequence, err = q.latestSequence(ctx, loginPolicyTable) + factors.State, err = q.latestState(ctx, loginPolicyTable) return factors, err } @@ -356,7 +360,7 @@ func (q *Queries) DefaultMultiFactors(ctx context.Context) (factors *MultiFactor if err != nil { return nil, err } - factors.LatestSequence, err = q.latestSequence(ctx, loginPolicyTable) + factors.State, err = q.latestState(ctx, loginPolicyTable) return factors, err } diff --git a/internal/query/login_policy_test.go b/internal/query/login_policy_test.go index 6da7e3487..f346bd9e3 100644 --- a/internal/query/login_policy_test.go +++ b/internal/query/login_policy_test.go @@ -129,8 +129,8 @@ func Test_LoginPolicyPrepares(t *testing.T) { true, true, true, - database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, - database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, + database.Array[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, + database.Array[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, domain.PasswordlessTypeAllowed, true, true, @@ -157,8 +157,8 @@ func Test_LoginPolicyPrepares(t *testing.T) { AllowExternalIDPs: true, ForceMFA: true, ForceMFALocalOnly: true, - SecondFactors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, - MultiFactors: database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, + SecondFactors: database.Array[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, + MultiFactors: database.Array[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, PasswordlessType: domain.PasswordlessTypeAllowed, IsDefault: true, HidePasswordReset: true, @@ -217,7 +217,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { regexp.QuoteMeta(prepareLoginPolicy2FAsStmt), prepareLoginPolicy2FAsCols, []driver.Value{ - database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, + database.Array[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, }, ), }, @@ -225,7 +225,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { SearchResponse: SearchResponse{ Count: 1, }, - Factors: database.EnumArray[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, + Factors: database.Array[domain.SecondFactorType]{domain.SecondFactorTypeTOTP}, }, }, { @@ -236,11 +236,11 @@ func Test_LoginPolicyPrepares(t *testing.T) { regexp.QuoteMeta(prepareLoginPolicy2FAsStmt), prepareLoginPolicy2FAsCols, []driver.Value{ - database.EnumArray[domain.SecondFactorType]{}, + database.Array[domain.SecondFactorType]{}, }, ), }, - object: &SecondFactors{Factors: database.EnumArray[domain.SecondFactorType]{}}, + object: &SecondFactors{Factors: database.Array[domain.SecondFactorType]{}}, }, { name: "prepareLoginPolicy2FAsQuery sql err", @@ -285,7 +285,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { regexp.QuoteMeta(prepareLoginPolicyMFAsStmt), prepareLoginPolicyMFAsCols, []driver.Value{ - database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, + database.Array[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, }, ), }, @@ -293,7 +293,7 @@ func Test_LoginPolicyPrepares(t *testing.T) { SearchResponse: SearchResponse{ Count: 1, }, - Factors: database.EnumArray[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, + Factors: database.Array[domain.MultiFactorType]{domain.MultiFactorTypeU2FWithPIN}, }, }, { @@ -304,11 +304,11 @@ func Test_LoginPolicyPrepares(t *testing.T) { regexp.QuoteMeta(prepareLoginPolicyMFAsStmt), prepareLoginPolicyMFAsCols, []driver.Value{ - database.EnumArray[domain.MultiFactorType]{}, + database.Array[domain.MultiFactorType]{}, }, ), }, - object: &MultiFactors{Factors: database.EnumArray[domain.MultiFactorType]{}}, + object: &MultiFactors{Factors: database.Array[domain.MultiFactorType]{}}, }, { name: "prepareLoginPolicyMFAsQuery sql err", diff --git a/internal/query/member.go b/internal/query/member.go index f2f99ff93..c343ea65a 100644 --- a/internal/query/member.go +++ b/internal/query/member.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/query/projection" sq "github.com/Masterminds/squirrel" ) @@ -36,10 +35,10 @@ func NewMemberLastNameSearchQuery(method TextComparison, value string) (SearchQu } func NewMemberUserIDSearchQuery(value string) (SearchQuery, error) { - return NewTextQuery(memberUserID, value, TextEquals) + return NewTextQuery(membershipUserID, value, TextEquals) } func NewMemberResourceOwnerSearchQuery(value string) (SearchQuery, error) { - return NewTextQuery(memberResourceOwner, value, TextEquals) + return NewTextQuery(membershipResourceOwner, value, TextEquals) } type Members struct { @@ -54,7 +53,7 @@ type Member struct { ResourceOwner string UserID string - Roles database.StringArray + Roles database.TextArray[string] PreferredLoginName string Email string FirstName string @@ -63,19 +62,3 @@ type Member struct { AvatarURL string UserType domain.UserType } - -var ( - memberTableAlias = table{ - name: "members", - alias: "members", - instanceIDCol: projection.MemberInstanceID, - } - memberUserID = Column{ - name: projection.MemberUserIDCol, - table: memberTableAlias, - } - memberResourceOwner = Column{ - name: projection.MemberResourceOwner, - table: memberTableAlias, - } -) diff --git a/internal/query/milestone.go b/internal/query/milestone.go index e781151d7..7a91223c9 100644 --- a/internal/query/milestone.go +++ b/internal/query/milestone.go @@ -88,7 +88,7 @@ func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, qu return nil, err } - milestones.LatestSequence, err = q.latestSequence(ctx, milestonesTable) + milestones.State, err = q.latestState(ctx, milestonesTable) return milestones, err } diff --git a/internal/query/notification_policy.go b/internal/query/notification_policy.go index c015c1c96..cd637ca59 100644 --- a/internal/query/notification_policy.go +++ b/internal/query/notification_policy.go @@ -12,6 +12,7 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -81,7 +82,7 @@ func (q *Queries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx, err = projection.NotificationPolicyProjection.TriggerErr(ctx) + ctx, err = projection.NotificationPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) if err != nil { return nil, err } @@ -116,7 +117,7 @@ func (q *Queries) DefaultNotificationPolicy(ctx context.Context, shouldTriggerBu defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx, err = projection.NotificationPolicyProjection.TriggerErr(ctx) + ctx, err = projection.NotificationPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) if err != nil { return nil, err } diff --git a/internal/query/org.go b/internal/query/org.go index e99878f63..bd5a36d2e 100644 --- a/internal/query/org.go +++ b/internal/query/org.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" domain_pkg "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -94,7 +97,8 @@ func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.OrgProjection.Trigger(ctx) + ctx, err = projection.OrgProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := prepareOrgQuery(ctx, q.client) @@ -231,7 +235,7 @@ func (q *Queries) SearchOrgs(ctx context.Context, queries *OrgSearchQueries) (or return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.Internal") } - orgs.LatestSequence, err = q.latestSequence(ctx, orgsTable) + orgs.State, err = q.latestState(ctx, orgsTable) return orgs, err } diff --git a/internal/query/org_domain.go b/internal/query/org_domain.go index ee89efba9..d03f0291d 100644 --- a/internal/query/org_domain.go +++ b/internal/query/org_domain.go @@ -78,7 +78,7 @@ func (q *Queries) SearchOrgDomains(ctx context.Context, queries *OrgDomainSearch return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.Internal") } - domains.LatestSequence, err = q.latestSequence(ctx, orgDomainsTable) + domains.State, err = q.latestState(ctx, orgDomainsTable) return domains, err } diff --git a/internal/query/org_member.go b/internal/query/org_member.go index 3b5eb4a41..6b31eb27d 100644 --- a/internal/query/org_member.go +++ b/internal/query/org_member.go @@ -93,7 +93,7 @@ func (q *Queries) OrgMembers(ctx context.Context, queries *OrgMembersQuery, with return nil, errors.ThrowInvalidArgument(err, "QUERY-PDAVB", "Errors.Query.InvalidRequest") } - currentSequence, err := q.latestSequence(ctx, orgsTable) + currentSequence, err := q.latestState(ctx, orgsTable) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func (q *Queries) OrgMembers(ctx context.Context, queries *OrgMembersQuery, with return nil, errors.ThrowInternal(err, "QUERY-5g4yV", "Errors.Internal") } - members.LatestSequence = currentSequence + members.State = currentSequence return members, err } diff --git a/internal/query/org_member_test.go b/internal/query/org_member_test.go index 57e9f84b6..ee599b0e5 100644 --- a/internal/query/org_member_test.go +++ b/internal/query/org_member_test.go @@ -102,7 +102,7 @@ func Test_OrgMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -126,7 +126,7 @@ func Test_OrgMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -152,7 +152,7 @@ func Test_OrgMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -176,7 +176,7 @@ func Test_OrgMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", @@ -202,7 +202,7 @@ func Test_OrgMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-1", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -218,7 +218,7 @@ func Test_OrgMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-2", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -242,7 +242,7 @@ func Test_OrgMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-1", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -257,7 +257,7 @@ func Test_OrgMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-2", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", diff --git a/internal/query/org_metadata.go b/internal/query/org_metadata.go index 517d2d634..2d0311a90 100644 --- a/internal/query/org_metadata.go +++ b/internal/query/org_metadata.go @@ -8,9 +8,12 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -82,7 +85,8 @@ func (q *Queries) GetOrgMetadataByKey(ctx context.Context, shouldTriggerBulk boo defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.OrgMetadataProjection.Trigger(ctx) + ctx, err = projection.OrgMetadataProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := prepareOrgMetadataQuery(ctx, q.client) @@ -114,7 +118,8 @@ func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.OrgMetadataProjection.Trigger(ctx) + ctx, err = projection.OrgMetadataProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{ OrgMetadataOrgIDCol.identifier(): orgID, @@ -137,7 +142,7 @@ func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, return nil, errors.ThrowInternal(err, "QUERY-Ho2wf", "Errors.Internal") } - metadata.LatestSequence, err = q.latestSequence(ctx, orgMetadataTable) + metadata.State, err = q.latestState(ctx, orgMetadataTable) return metadata, err } diff --git a/internal/query/password_age_policy.go b/internal/query/password_age_policy.go index e2130f776..16104967c 100644 --- a/internal/query/password_age_policy.go +++ b/internal/query/password_age_policy.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -86,7 +89,8 @@ func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, shouldTriggerBulk defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PasswordAgeProjection.Trigger(ctx) + ctx, err = projection.PasswordAgeProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{PasswordAgeColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} if !withOwnerRemoved { @@ -119,7 +123,8 @@ func (q *Queries) DefaultPasswordAgePolicy(ctx context.Context, shouldTriggerBul defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PasswordAgeProjection.Trigger(ctx) + ctx, err = projection.PasswordAgeProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := preparePasswordAgePolicyQuery(ctx, q.client) diff --git a/internal/query/password_complexity_policy.go b/internal/query/password_complexity_policy.go index a61048b99..4e1942038 100644 --- a/internal/query/password_complexity_policy.go +++ b/internal/query/password_complexity_policy.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -38,7 +41,8 @@ func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, shouldTrigg defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PasswordComplexityProjection.Trigger(ctx) + ctx, err = projection.PasswordComplexityProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{PasswordComplexityColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} if !withOwnerRemoved { @@ -71,7 +75,8 @@ func (q *Queries) DefaultPasswordComplexityPolicy(ctx context.Context, shouldTri defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PasswordComplexityProjection.Trigger(ctx) + ctx, err = projection.PasswordComplexityProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := preparePasswordComplexityPolicyQuery(ctx, q.client) diff --git a/internal/query/privacy_policy.go b/internal/query/privacy_policy.go index 8ff9f0edb..f37884ca1 100644 --- a/internal/query/privacy_policy.go +++ b/internal/query/privacy_policy.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -96,7 +99,8 @@ func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, shouldTriggerBulk bool defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PrivacyPolicyProjection.Trigger(ctx) + ctx, err = projection.PrivacyPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{PrivacyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} if !withOwnerRemoved { @@ -128,7 +132,8 @@ func (q *Queries) DefaultPrivacyPolicy(ctx context.Context, shouldTriggerBulk bo defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PrivacyPolicyProjection.Trigger(ctx) + ctx, err = projection.PrivacyPolicyProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := preparePrivacyPolicyQuery(ctx, q.client) diff --git a/internal/query/project.go b/internal/query/project.go index 0e628d7b1..9c6301684 100644 --- a/internal/query/project.go +++ b/internal/query/project.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -105,7 +108,8 @@ func (q *Queries) ProjectByID(ctx context.Context, shouldTriggerBulk bool, id st defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.ProjectProjection.Trigger(ctx) + ctx, err = projection.ProjectProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := prepareProjectQuery(ctx, q.client) @@ -149,7 +153,7 @@ func (q *Queries) SearchProjects(ctx context.Context, queries *ProjectSearchQuer if err != nil { return nil, errors.ThrowInternal(err, "QUERY-2j00f", "Errors.Internal") } - projects.LatestSequence, err = q.latestSequence(ctx, projectsTable) + projects.State, err = q.latestState(ctx, projectsTable) return projects, err } diff --git a/internal/query/project_grant.go b/internal/query/project_grant.go index d7ee5ce1a..229d3b320 100644 --- a/internal/query/project_grant.go +++ b/internal/query/project_grant.go @@ -8,11 +8,14 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -102,7 +105,7 @@ type ProjectGrant struct { ProjectName string GrantedOrgID string OrgName string - GrantedRoleKeys database.StringArray + GrantedRoleKeys database.TextArray[string] ResourceOwnerName string } @@ -116,7 +119,8 @@ func (q *Queries) ProjectGrantByID(ctx context.Context, shouldTriggerBulk bool, defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.ProjectGrantProjection.Trigger(ctx) + ctx, err = projection.ProjectGrantProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } stmt, scan := prepareProjectGrantQuery(ctx, q.client) @@ -191,7 +195,7 @@ func (q *Queries) SearchProjectGrants(ctx context.Context, queries *ProjectGrant return nil, errors.ThrowInternal(err, "QUERY-PP02n", "Errors.Internal") } - grants.LatestSequence, err = q.latestSequence(ctx, projectGrantsTable) + grants.State, err = q.latestState(ctx, projectGrantsTable) return grants, err } diff --git a/internal/query/project_grant_member.go b/internal/query/project_grant_member.go index 11de7cd0e..a6ee8dc29 100644 --- a/internal/query/project_grant_member.go +++ b/internal/query/project_grant_member.go @@ -107,7 +107,7 @@ func (q *Queries) ProjectGrantMembers(ctx context.Context, queries *ProjectGrant return nil, errors.ThrowInvalidArgument(err, "QUERY-USNwM", "Errors.Query.InvalidRequest") } - currentSequence, err := q.latestSequence(ctx, projectGrantMemberTable) + currentSequence, err := q.latestState(ctx, projectGrantMemberTable) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (q *Queries) ProjectGrantMembers(ctx context.Context, queries *ProjectGrant return nil, errors.ThrowInternal(err, "QUERY-Pdg1I", "Errors.Internal") } - members.LatestSequence = currentSequence + members.State = currentSequence return members, err } diff --git a/internal/query/project_grant_member_test.go b/internal/query/project_grant_member_test.go index 99e7c5320..adb1c506e 100644 --- a/internal/query/project_grant_member_test.go +++ b/internal/query/project_grant_member_test.go @@ -105,7 +105,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -129,7 +129,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -155,7 +155,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -179,7 +179,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", @@ -205,7 +205,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-1", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -221,7 +221,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-2", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -245,7 +245,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-1", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -260,7 +260,7 @@ func Test_ProjectGrantMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-2", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", diff --git a/internal/query/project_grant_test.go b/internal/query/project_grant_test.go index fbb3432b6..e67d2ad17 100644 --- a/internal/query/project_grant_test.go +++ b/internal/query/project_grant_test.go @@ -122,7 +122,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, }, @@ -144,7 +144,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -169,7 +169,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { nil, "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, }, @@ -191,7 +191,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -216,7 +216,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", nil, - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, }, @@ -238,7 +238,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -263,7 +263,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, nil, }, }, @@ -285,7 +285,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "", }, }, @@ -310,7 +310,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, { @@ -324,7 +324,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, }, @@ -346,7 +346,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, { @@ -360,7 +360,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -419,7 +419,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, ), @@ -435,7 +435,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -457,7 +457,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", nil, - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, ), @@ -473,7 +473,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, @@ -495,7 +495,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { "project-name", "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, nil, }, ), @@ -511,7 +511,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "project-name", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "", }, }, @@ -533,7 +533,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { nil, "org-id", "org-name", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, "ro-name", }, ), @@ -549,7 +549,7 @@ func Test_ProjectGrantPrepares(t *testing.T) { ProjectName: "", GrantedOrgID: "org-id", OrgName: "org-name", - GrantedRoleKeys: database.StringArray{"role-key"}, + GrantedRoleKeys: database.TextArray[string]{"role-key"}, ResourceOwnerName: "ro-name", }, }, diff --git a/internal/query/project_member.go b/internal/query/project_member.go index 3cdb30086..47c937be5 100644 --- a/internal/query/project_member.go +++ b/internal/query/project_member.go @@ -93,7 +93,7 @@ func (q *Queries) ProjectMembers(ctx context.Context, queries *ProjectMembersQue return nil, errors.ThrowInvalidArgument(err, "QUERY-T8CuT", "Errors.Query.InvalidRequest") } - currentSequence, err := q.latestSequence(ctx, projectMemberTable) + currentSequence, err := q.latestState(ctx, projectMemberTable) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func (q *Queries) ProjectMembers(ctx context.Context, queries *ProjectMembersQue return nil, errors.ThrowInternal(err, "QUERY-uh6pj", "Errors.Internal") } - members.LatestSequence = currentSequence + members.State = currentSequence return members, err } diff --git a/internal/query/project_member_test.go b/internal/query/project_member_test.go index f91771889..f102cff63 100644 --- a/internal/query/project_member_test.go +++ b/internal/query/project_member_test.go @@ -102,7 +102,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -126,7 +126,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -152,7 +152,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -176,7 +176,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", @@ -202,7 +202,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-1", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "gigi@caos-ag.zitadel.ch", "gigi@caos.ch", "first-name", @@ -218,7 +218,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { uint64(20211206), "ro", "user-id-2", - database.StringArray{"role-1", "role-2"}, + database.TextArray[string]{"role-1", "role-2"}, "machine@caos-ag.zitadel.ch", nil, nil, @@ -242,7 +242,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-1", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "gigi@caos-ag.zitadel.ch", Email: "gigi@caos.ch", FirstName: "first-name", @@ -257,7 +257,7 @@ func Test_ProjectMemberPrepares(t *testing.T) { Sequence: 20211206, ResourceOwner: "ro", UserID: "user-id-2", - Roles: database.StringArray{"role-1", "role-2"}, + Roles: database.TextArray[string]{"role-1", "role-2"}, PreferredLoginName: "machine@caos-ag.zitadel.ch", Email: "", FirstName: "", diff --git a/internal/query/project_role.go b/internal/query/project_role.go index fbe802714..c141ed918 100644 --- a/internal/query/project_role.go +++ b/internal/query/project_role.go @@ -7,9 +7,12 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -88,7 +91,8 @@ func (q *Queries) SearchProjectRoles(ctx context.Context, shouldTriggerBulk bool defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.ProjectRoleProjection.Trigger(ctx) + ctx, err = projection.ProjectRoleProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } eq := sq.Eq{ProjectRoleColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} @@ -109,7 +113,7 @@ func (q *Queries) SearchProjectRoles(ctx context.Context, shouldTriggerBulk bool if err != nil { return nil, errors.ThrowInternal(err, "QUERY-5Ngd9", "Errors.Internal") } - roles.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) + roles.State, err = q.latestState(ctx, projectRolesTable) return roles, err } @@ -145,7 +149,7 @@ func (q *Queries) SearchGrantedProjectRoles(ctx context.Context, grantID, grante return nil, errors.ThrowInternal(err, "QUERY-5Ngd9", "Errors.Internal") } - roles.LatestSequence, err = q.latestSequence(ctx, projectRolesTable) + roles.State, err = q.latestState(ctx, projectRolesTable) return roles, err } diff --git a/internal/query/projection/action.go b/internal/query/projection/action.go index 780aee2d9..0bdfccce0 100644 --- a/internal/query/projection/action.go +++ b/internal/query/projection/action.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/action" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -29,43 +29,44 @@ const ( ActionOwnerRemovedCol = "owner_removed" ) -type actionProjection struct { - crdb.StatementHandler +type actionProjection struct{} + +func newActionProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(actionProjection)) } -func newActionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *actionProjection { - p := new(actionProjection) - config.ProjectionName = ActionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(ActionIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ActionCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ActionChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ActionResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(ActionInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ActionStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(ActionSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(ActionNameCol, crdb.ColumnTypeText), - crdb.NewColumn(ActionScriptCol, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(ActionTimeoutCol, crdb.ColumnTypeInt64, crdb.Default(0)), - crdb.NewColumn(ActionAllowedToFailCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(ActionOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*actionProjection) Name() string { + return ActionTable +} + +func (*actionProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(ActionIDCol, handler.ColumnTypeText), + handler.NewColumn(ActionCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(ActionChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(ActionResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(ActionInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(ActionStateCol, handler.ColumnTypeEnum), + handler.NewColumn(ActionSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(ActionNameCol, handler.ColumnTypeText), + handler.NewColumn(ActionScriptCol, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(ActionTimeoutCol, handler.ColumnTypeInt64, handler.Default(0)), + handler.NewColumn(ActionAllowedToFailCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(ActionOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(ActionInstanceIDCol, ActionIDCol), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{ActionResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{ActionOwnerRemovedCol})), + handler.NewPrimaryKey(ActionInstanceIDCol, ActionIDCol), + handler.WithIndex(handler.NewIndex("resource_owner", []string{ActionResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{ActionOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *actionProjection) reducers() []handler.AggregateReducer { +func (p *actionProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: action.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: action.AddedEventType, Reduce: p.reduceActionAdded, @@ -90,7 +91,7 @@ func (p *actionProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -99,7 +100,7 @@ func (p *actionProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(ActionInstanceIDCol), @@ -114,7 +115,7 @@ func (p *actionProjection) reduceActionAdded(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dff21", "reduce.wrong.event.type% s", action.AddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(ActionIDCol, e.Aggregate().ID), @@ -153,7 +154,7 @@ func (p *actionProjection) reduceActionChanged(event eventstore.Event) (*handler if e.AllowedToFail != nil { values = append(values, handler.NewCol(ActionAllowedToFailCol, *e.AllowedToFail)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, values, []handler.Condition{ @@ -168,7 +169,7 @@ func (p *actionProjection) reduceActionDeactivated(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Fgh32", "reduce.wrong.event.type %s", action.DeactivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ActionChangeDateCol, e.CreationDate()), @@ -187,7 +188,7 @@ func (p *actionProjection) reduceActionReactivated(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-hwdqa", "reduce.wrong.event.type %s", action.ReactivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ActionChangeDateCol, e.CreationDate()), @@ -206,7 +207,7 @@ func (p *actionProjection) reduceActionRemoved(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dgh2d", "reduce.wrong.event.type %s", action.RemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ActionIDCol, e.Aggregate().ID), @@ -220,7 +221,7 @@ func (p *actionProjection) reduceOwnerRemoved(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-mSmWM", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ActionInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/action_test.go b/internal/query/projection/action_test.go index 2c235281e..71dacb9d8 100644 --- a/internal/query/projection/action_test.go +++ b/internal/query/projection/action_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/action" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -27,17 +26,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceActionAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(action.AddedEventType), - action.AggregateType, - []byte(`{"name": "name", "script":"name(){}","timeout": 3000000000, "allowedToFail": true}`), - ), action.AddedEventMapper), + event: getEvent( + testEvent( + action.AddedEventType, + action.AggregateType, + []byte(`{"name": "name", "script":"name(){}","timeout": 3000000000, "allowedToFail": true}`), + ), + action.AddedEventMapper, + ), }, reduce: (&actionProjection{}).reduceActionAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("action"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("action"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -63,17 +64,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceActionChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(action.ChangedEventType), - action.AggregateType, - []byte(`{"name": "name2", "script":"name2(){}"}`), - ), action.ChangedEventMapper), + event: getEvent( + testEvent( + action.ChangedEventType, + action.AggregateType, + []byte(`{"name": "name2", "script":"name2(){}"}`), + ), + action.ChangedEventMapper, + ), }, reduce: (&actionProjection{}).reduceActionChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("action"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("action"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -94,17 +97,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceActionDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(action.ChangedEventType), - action.AggregateType, - []byte(`{}`), - ), action.DeactivatedEventMapper), + event: getEvent( + testEvent( + action.ChangedEventType, + action.AggregateType, + []byte(`{}`), + ), + action.DeactivatedEventMapper, + ), }, reduce: (&actionProjection{}).reduceActionDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("action"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("action"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -124,17 +129,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceActionReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(action.ChangedEventType), - action.AggregateType, - []byte(`{}`), - ), action.ReactivatedEventMapper), + event: getEvent( + testEvent( + action.ChangedEventType, + action.AggregateType, + []byte(`{}`), + ), + action.ReactivatedEventMapper, + ), }, reduce: (&actionProjection{}).reduceActionReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("action"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("action"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -154,17 +161,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceActionRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(action.ChangedEventType), - action.AggregateType, - []byte(`{}`), - ), action.RemovedEventMapper), + event: getEvent( + testEvent( + action.ChangedEventType, + action.AggregateType, + []byte(`{}`), + ), + action.RemovedEventMapper, + ), }, reduce: (&actionProjection{}).reduceActionRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("action"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("action"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -181,17 +190,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceOwnerRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), + org.OrgRemovedEventMapper, + ), }, reduce: (&actionProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -208,17 +219,19 @@ func TestActionProjection_reduces(t *testing.T) { { name: "reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), + instance.InstanceRemovedEventMapper, + ), }, reduce: reduceInstanceRemovedHelper(ActionInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/app.go b/internal/query/projection/app.go index 8ac53c10b..3ed632c23 100644 --- a/internal/query/projection/app.go +++ b/internal/query/projection/app.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -67,91 +67,92 @@ const ( AppSAMLConfigColumnMetadataURL = "metadata_url" ) -type appProjection struct { - crdb.StatementHandler +type appProjection struct{} + +func newAppProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(appProjection)) } -func newAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) *appProjection { - p := new(appProjection) - config.ProjectionName = AppProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(AppColumnID, crdb.ColumnTypeText), - crdb.NewColumn(AppColumnName, crdb.ColumnTypeText), - crdb.NewColumn(AppColumnProjectID, crdb.ColumnTypeText), - crdb.NewColumn(AppColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AppColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AppColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(AppColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(AppColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(AppColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(AppColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*appProjection) Name() string { + return AppProjectionTable +} + +func (*appProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(AppColumnID, handler.ColumnTypeText), + handler.NewColumn(AppColumnName, handler.ColumnTypeText), + handler.NewColumn(AppColumnProjectID, handler.ColumnTypeText), + handler.NewColumn(AppColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(AppColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(AppColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(AppColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(AppColumnState, handler.ColumnTypeEnum), + handler.NewColumn(AppColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(AppColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(AppColumnInstanceID, AppColumnID), - crdb.WithIndex(crdb.NewIndex("project_id", []string{AppColumnProjectID})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{AppColumnOwnerRemoved})), + handler.NewPrimaryKey(AppColumnInstanceID, AppColumnID), + handler.WithIndex(handler.NewIndex("project_id", []string{AppColumnProjectID})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{AppColumnOwnerRemoved})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppAPIConfigColumnAppID, crdb.ColumnTypeText), - crdb.NewColumn(AppAPIConfigColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(AppAPIConfigColumnClientID, crdb.ColumnTypeText), - crdb.NewColumn(AppAPIConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()), - crdb.NewColumn(AppAPIConfigColumnAuthMethod, crdb.ColumnTypeEnum), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(AppAPIConfigColumnAppID, handler.ColumnTypeText), + handler.NewColumn(AppAPIConfigColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(AppAPIConfigColumnClientID, handler.ColumnTypeText), + handler.NewColumn(AppAPIConfigColumnClientSecret, handler.ColumnTypeJSONB, handler.Nullable()), + handler.NewColumn(AppAPIConfigColumnAuthMethod, handler.ColumnTypeEnum), }, - crdb.NewPrimaryKey(AppAPIConfigColumnInstanceID, AppAPIConfigColumnAppID), + handler.NewPrimaryKey(AppAPIConfigColumnInstanceID, AppAPIConfigColumnAppID), appAPITableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - crdb.WithIndex(crdb.NewIndex("client_id", []string{AppAPIConfigColumnClientID})), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + handler.WithIndex(handler.NewIndex("client_id", []string{AppAPIConfigColumnClientID})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppOIDCConfigColumnAppID, crdb.ColumnTypeText), - crdb.NewColumn(AppOIDCConfigColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(AppOIDCConfigColumnVersion, crdb.ColumnTypeEnum), - crdb.NewColumn(AppOIDCConfigColumnClientID, crdb.ColumnTypeText), - crdb.NewColumn(AppOIDCConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnRedirectUris, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnResponseTypes, crdb.ColumnTypeEnumArray, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnGrantTypes, crdb.ColumnTypeEnumArray, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnApplicationType, crdb.ColumnTypeEnum), - crdb.NewColumn(AppOIDCConfigColumnAuthMethodType, crdb.ColumnTypeEnum), - crdb.NewColumn(AppOIDCConfigColumnPostLogoutRedirectUris, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnDevMode, crdb.ColumnTypeBool), - crdb.NewColumn(AppOIDCConfigColumnAccessTokenType, crdb.ColumnTypeEnum), - crdb.NewColumn(AppOIDCConfigColumnAccessTokenRoleAssertion, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(AppOIDCConfigColumnIDTokenRoleAssertion, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(AppOIDCConfigColumnIDTokenUserinfoAssertion, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(AppOIDCConfigColumnClockSkew, crdb.ColumnTypeInt64, crdb.Default(0)), - crdb.NewColumn(AppOIDCConfigColumnAdditionalOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, crdb.ColumnTypeBool, crdb.Default(false)), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(AppOIDCConfigColumnAppID, handler.ColumnTypeText), + handler.NewColumn(AppOIDCConfigColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(AppOIDCConfigColumnVersion, handler.ColumnTypeEnum), + handler.NewColumn(AppOIDCConfigColumnClientID, handler.ColumnTypeText), + handler.NewColumn(AppOIDCConfigColumnClientSecret, handler.ColumnTypeJSONB, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnRedirectUris, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnResponseTypes, handler.ColumnTypeEnumArray, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnGrantTypes, handler.ColumnTypeEnumArray, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnApplicationType, handler.ColumnTypeEnum), + handler.NewColumn(AppOIDCConfigColumnAuthMethodType, handler.ColumnTypeEnum), + handler.NewColumn(AppOIDCConfigColumnPostLogoutRedirectUris, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnDevMode, handler.ColumnTypeBool), + handler.NewColumn(AppOIDCConfigColumnAccessTokenType, handler.ColumnTypeEnum), + handler.NewColumn(AppOIDCConfigColumnAccessTokenRoleAssertion, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(AppOIDCConfigColumnIDTokenRoleAssertion, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(AppOIDCConfigColumnIDTokenUserinfoAssertion, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(AppOIDCConfigColumnClockSkew, handler.ColumnTypeInt64, handler.Default(0)), + handler.NewColumn(AppOIDCConfigColumnAdditionalOrigins, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID), + handler.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID), appOIDCTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - crdb.WithIndex(crdb.NewIndex("client_id", []string{AppOIDCConfigColumnClientID})), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + handler.WithIndex(handler.NewIndex("client_id", []string{AppOIDCConfigColumnClientID})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppSAMLConfigColumnAppID, crdb.ColumnTypeText), - crdb.NewColumn(AppSAMLConfigColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(AppSAMLConfigColumnEntityID, crdb.ColumnTypeText), - crdb.NewColumn(AppSAMLConfigColumnMetadata, crdb.ColumnTypeBytes), - crdb.NewColumn(AppSAMLConfigColumnMetadataURL, crdb.ColumnTypeText), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(AppSAMLConfigColumnAppID, handler.ColumnTypeText), + handler.NewColumn(AppSAMLConfigColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(AppSAMLConfigColumnEntityID, handler.ColumnTypeText), + handler.NewColumn(AppSAMLConfigColumnMetadata, handler.ColumnTypeBytes), + handler.NewColumn(AppSAMLConfigColumnMetadataURL, handler.ColumnTypeText), }, - crdb.NewPrimaryKey(AppSAMLConfigColumnInstanceID, AppSAMLConfigColumnAppID), + handler.NewPrimaryKey(AppSAMLConfigColumnInstanceID, AppSAMLConfigColumnAppID), appSAMLTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - crdb.WithIndex(crdb.NewIndex("entity_id", []string{AppSAMLConfigColumnEntityID})), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + handler.WithIndex(handler.NewIndex("entity_id", []string{AppSAMLConfigColumnEntityID})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *appProjection) reducers() []handler.AggregateReducer { +func (p *appProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.ApplicationAddedType, Reduce: p.reduceAppAdded, @@ -212,7 +213,7 @@ func (p *appProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -221,7 +222,7 @@ func (p *appProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(AppColumnInstanceID), @@ -236,7 +237,7 @@ func (p *appProjection) reduceAppAdded(event eventstore.Event) (*handler.Stateme if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-1xYE6", "reduce.wrong.event.type %s", project.ApplicationAddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(AppColumnID, e.AppID), @@ -258,9 +259,9 @@ func (p *appProjection) reduceAppChanged(event eventstore.Event) (*handler.State return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ZJ8JA", "reduce.wrong.event.type %s", project.ApplicationChangedType) } if e.Name == "" { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(AppColumnName, e.Name), @@ -279,7 +280,7 @@ func (p *appProjection) reduceAppDeactivated(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-MVWxZ", "reduce.wrong.event.type %s", project.ApplicationDeactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(AppColumnState, domain.AppStateInactive), @@ -298,7 +299,7 @@ func (p *appProjection) reduceAppReactivated(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-D0HZO", "reduce.wrong.event.type %s", project.ApplicationReactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(AppColumnState, domain.AppStateActive), @@ -317,7 +318,7 @@ func (p *appProjection) reduceAppRemoved(event eventstore.Event) (*handler.State if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Y99aq", "reduce.wrong.event.type %s", project.ApplicationRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), @@ -331,7 +332,7 @@ func (p *appProjection) reduceProjectRemoved(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DlUlO", "reduce.wrong.event.type %s", project.ProjectRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(AppColumnProjectID, e.Aggregate().ID), @@ -345,9 +346,9 @@ func (p *appProjection) reduceAPIConfigAdded(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Y99aq", "reduce.wrong.event.type %s", project.APIConfigAddedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AppAPIConfigColumnAppID, e.AppID), handler.NewCol(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), @@ -355,9 +356,9 @@ func (p *appProjection) reduceAPIConfigAdded(event eventstore.Event) (*handler.S handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret), handler.NewCol(AppAPIConfigColumnAuthMethod, e.AuthMethodType), }, - crdb.WithTableSuffix(appAPITableSuffix), + handler.WithTableSuffix(appAPITableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -383,19 +384,19 @@ func (p *appProjection) reduceAPIConfigChanged(event eventstore.Event) (*handler cols = append(cols, handler.NewCol(AppAPIConfigColumnAuthMethod, *e.AuthMethodType)) } if len(cols) == 0 { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(AppAPIConfigColumnAppID, e.AppID), handler.NewCond(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(appAPITableSuffix), + handler.WithTableSuffix(appAPITableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -413,9 +414,9 @@ func (p *appProjection) reduceAPIConfigSecretChanged(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ttb0I", "reduce.wrong.event.type %s", project.APIConfigSecretChangedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret), }, @@ -423,9 +424,9 @@ func (p *appProjection) reduceAPIConfigSecretChanged(event eventstore.Event) (*h handler.NewCond(AppAPIConfigColumnAppID, e.AppID), handler.NewCond(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(appAPITableSuffix), + handler.WithTableSuffix(appAPITableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -443,33 +444,33 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-GNHU1", "reduce.wrong.event.type %s", project.OIDCConfigAddedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AppOIDCConfigColumnAppID, e.AppID), handler.NewCol(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(AppOIDCConfigColumnVersion, e.Version), handler.NewCol(AppOIDCConfigColumnClientID, e.ClientID), handler.NewCol(AppOIDCConfigColumnClientSecret, e.ClientSecret), - handler.NewCol(AppOIDCConfigColumnRedirectUris, database.StringArray(e.RedirectUris)), - handler.NewCol(AppOIDCConfigColumnResponseTypes, database.EnumArray[domain.OIDCResponseType](e.ResponseTypes)), - handler.NewCol(AppOIDCConfigColumnGrantTypes, database.EnumArray[domain.OIDCGrantType](e.GrantTypes)), + handler.NewCol(AppOIDCConfigColumnRedirectUris, database.TextArray[string](e.RedirectUris)), + handler.NewCol(AppOIDCConfigColumnResponseTypes, database.Array[domain.OIDCResponseType](e.ResponseTypes)), + handler.NewCol(AppOIDCConfigColumnGrantTypes, database.Array[domain.OIDCGrantType](e.GrantTypes)), handler.NewCol(AppOIDCConfigColumnApplicationType, e.ApplicationType), handler.NewCol(AppOIDCConfigColumnAuthMethodType, e.AuthMethodType), - handler.NewCol(AppOIDCConfigColumnPostLogoutRedirectUris, database.StringArray(e.PostLogoutRedirectUris)), + handler.NewCol(AppOIDCConfigColumnPostLogoutRedirectUris, database.TextArray[string](e.PostLogoutRedirectUris)), handler.NewCol(AppOIDCConfigColumnDevMode, e.DevMode), handler.NewCol(AppOIDCConfigColumnAccessTokenType, e.AccessTokenType), handler.NewCol(AppOIDCConfigColumnAccessTokenRoleAssertion, e.AccessTokenRoleAssertion), handler.NewCol(AppOIDCConfigColumnIDTokenRoleAssertion, e.IDTokenRoleAssertion), handler.NewCol(AppOIDCConfigColumnIDTokenUserinfoAssertion, e.IDTokenUserinfoAssertion), handler.NewCol(AppOIDCConfigColumnClockSkew, e.ClockSkew), - handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(e.AdditionalOrigins)), + handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.TextArray[string](e.AdditionalOrigins)), handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, e.SkipNativeAppSuccessPage), }, - crdb.WithTableSuffix(appOIDCTableSuffix), + handler.WithTableSuffix(appOIDCTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -493,13 +494,13 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle cols = append(cols, handler.NewCol(AppOIDCConfigColumnVersion, *e.Version)) } if e.RedirectUris != nil { - cols = append(cols, handler.NewCol(AppOIDCConfigColumnRedirectUris, database.StringArray(*e.RedirectUris))) + cols = append(cols, handler.NewCol(AppOIDCConfigColumnRedirectUris, database.TextArray[string](*e.RedirectUris))) } if e.ResponseTypes != nil { - cols = append(cols, handler.NewCol(AppOIDCConfigColumnResponseTypes, database.EnumArray[domain.OIDCResponseType](*e.ResponseTypes))) + cols = append(cols, handler.NewCol(AppOIDCConfigColumnResponseTypes, database.Array[domain.OIDCResponseType](*e.ResponseTypes))) } if e.GrantTypes != nil { - cols = append(cols, handler.NewCol(AppOIDCConfigColumnGrantTypes, database.EnumArray[domain.OIDCGrantType](*e.GrantTypes))) + cols = append(cols, handler.NewCol(AppOIDCConfigColumnGrantTypes, database.Array[domain.OIDCGrantType](*e.GrantTypes))) } if e.ApplicationType != nil { cols = append(cols, handler.NewCol(AppOIDCConfigColumnApplicationType, *e.ApplicationType)) @@ -508,7 +509,7 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle cols = append(cols, handler.NewCol(AppOIDCConfigColumnAuthMethodType, *e.AuthMethodType)) } if e.PostLogoutRedirectUris != nil { - cols = append(cols, handler.NewCol(AppOIDCConfigColumnPostLogoutRedirectUris, database.StringArray(*e.PostLogoutRedirectUris))) + cols = append(cols, handler.NewCol(AppOIDCConfigColumnPostLogoutRedirectUris, database.TextArray[string](*e.PostLogoutRedirectUris))) } if e.DevMode != nil { cols = append(cols, handler.NewCol(AppOIDCConfigColumnDevMode, *e.DevMode)) @@ -529,27 +530,27 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle cols = append(cols, handler.NewCol(AppOIDCConfigColumnClockSkew, *e.ClockSkew)) } if e.AdditionalOrigins != nil { - cols = append(cols, handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(*e.AdditionalOrigins))) + cols = append(cols, handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.TextArray[string](*e.AdditionalOrigins))) } if e.SkipNativeAppSuccessPage != nil { cols = append(cols, handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, *e.SkipNativeAppSuccessPage)) } if len(cols) == 0 { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(AppOIDCConfigColumnAppID, e.AppID), handler.NewCond(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(appOIDCTableSuffix), + handler.WithTableSuffix(appOIDCTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -567,9 +568,9 @@ func (p *appProjection) reduceOIDCConfigSecretChanged(event eventstore.Event) (* if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-GNHU1", "reduce.wrong.event.type %s", project.OIDCConfigSecretChangedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppOIDCConfigColumnClientSecret, e.ClientSecret), }, @@ -577,9 +578,9 @@ func (p *appProjection) reduceOIDCConfigSecretChanged(event eventstore.Event) (* handler.NewCond(AppOIDCConfigColumnAppID, e.AppID), handler.NewCond(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(appOIDCTableSuffix), + handler.WithTableSuffix(appOIDCTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -598,7 +599,7 @@ func (p *appProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.Sta return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Hyd1f", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), @@ -612,9 +613,9 @@ func (p *appProjection) reduceSAMLConfigAdded(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgument(nil, "HANDL-GMHU1", "reduce.wrong.event.type") } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AppSAMLConfigColumnAppID, e.AppID), handler.NewCol(AppSAMLConfigColumnInstanceID, e.Aggregate().InstanceID), @@ -622,9 +623,9 @@ func (p *appProjection) reduceSAMLConfigAdded(event eventstore.Event) (*handler. handler.NewCol(AppSAMLConfigColumnMetadata, e.Metadata), handler.NewCol(AppSAMLConfigColumnMetadataURL, e.MetadataURL), }, - crdb.WithTableSuffix(appSAMLTableSuffix), + handler.WithTableSuffix(appSAMLTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), @@ -655,20 +656,20 @@ func (p *appProjection) reduceSAMLConfigChanged(event eventstore.Event) (*handle } if len(cols) == 0 { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(AppSAMLConfigColumnAppID, e.AppID), handler.NewCond(AppSAMLConfigColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(appSAMLTableSuffix), + handler.WithTableSuffix(appSAMLTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(AppColumnChangeDate, e.CreationDate()), handler.NewCol(AppColumnSequence, e.Sequence()), diff --git a/internal/query/projection/app_test.go b/internal/query/projection/app_test.go index fe63c2991..f595d6a36 100644 --- a/internal/query/projection/app_test.go +++ b/internal/query/projection/app_test.go @@ -8,8 +8,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -28,20 +27,22 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationAddedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationAddedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "name": "my-app" }`), - ), project.ApplicationAddedEventMapper), + ), + project.ApplicationAddedEventMapper, + ), }, reduce: (&appProjection{}).reduceAppAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -65,20 +66,20 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "name": "my-app" }`), - ), project.ApplicationChangedEventMapper), + ), project.ApplicationChangedEventMapper), }, reduce: (&appProjection{}).reduceAppChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -98,19 +99,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppChanged no change", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.ApplicationChangedEventMapper), + ), project.ApplicationChangedEventMapper), }, reduce: (&appProjection{}).reduceAppChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -119,19 +120,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationDeactivatedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationDeactivatedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.ApplicationDeactivatedEventMapper), + ), project.ApplicationDeactivatedEventMapper), }, reduce: (&appProjection{}).reduceAppDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -151,19 +152,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationReactivatedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationReactivatedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.ApplicationReactivatedEventMapper), + ), project.ApplicationReactivatedEventMapper), }, reduce: (&appProjection{}).reduceAppReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -183,19 +184,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAppRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationRemovedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.ApplicationRemovedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.ApplicationRemovedEventMapper), + ), project.ApplicationRemovedEventMapper), }, reduce: (&appProjection{}).reduceAppRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -212,17 +213,17 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceProjectRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - []byte(`{}`), - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + []byte(`{}`), + ), project.ProjectRemovedEventMapper), }, reduce: (&appProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -239,17 +240,17 @@ func TestAppProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(AppColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -265,22 +266,22 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAPIConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigAddedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.APIConfigAddedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "clientId": "client-id", "clientSecret": {}, "authMethodType": 1 }`), - ), project.APIConfigAddedEventMapper), + ), project.APIConfigAddedEventMapper), }, reduce: (&appProjection{}).reduceAPIConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -309,22 +310,22 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAPIConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.APIConfigChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "clientId": "client-id", "clientSecret": {}, "authMethodType": 1 }`), - ), project.APIConfigChangedEventMapper), + ), project.APIConfigChangedEventMapper), }, reduce: (&appProjection{}).reduceAPIConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -352,19 +353,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAPIConfigChanged noop", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.APIConfigChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.APIConfigChangedEventMapper), + ), project.APIConfigChangedEventMapper), }, reduce: (&appProjection{}).reduceAPIConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -373,20 +374,20 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceAPIConfigSecretChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigSecretChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.APIConfigSecretChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "client_secret": {} }`), - ), project.APIConfigSecretChangedEventMapper), + ), project.APIConfigSecretChangedEventMapper), }, reduce: (&appProjection{}).reduceAPIConfigSecretChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -413,10 +414,11 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceOIDCConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigAddedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.OIDCConfigAddedType, + project.AggregateType, + []byte(`{ "oidcVersion": 0, "appId": "app-id", "clientId": "client-id", @@ -436,13 +438,12 @@ func TestAppProjection_reduces(t *testing.T) { "additionalOrigins": ["origin.one.ch", "origin.two.ch"], "skipNativeAppSuccessPage": true }`), - ), project.OIDCConfigAddedEventMapper), + ), project.OIDCConfigAddedEventMapper), }, reduce: (&appProjection{}).reduceOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -453,19 +454,19 @@ func TestAppProjection_reduces(t *testing.T) { domain.OIDCVersionV1, "client-id", anyArg{}, - database.StringArray{"redirect.one.ch", "redirect.two.ch"}, - database.EnumArray[domain.OIDCResponseType]{1, 2}, - database.EnumArray[domain.OIDCGrantType]{1, 2}, + database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"}, + database.Array[domain.OIDCResponseType]{1, 2}, + database.Array[domain.OIDCGrantType]{1, 2}, domain.OIDCApplicationTypeNative, domain.OIDCAuthMethodTypeNone, - database.StringArray{"logout.one.ch", "logout.two.ch"}, + database.TextArray[string]{"logout.one.ch", "logout.two.ch"}, true, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Microsecond, - database.StringArray{"origin.one.ch", "origin.two.ch"}, + database.TextArray[string]{"origin.one.ch", "origin.two.ch"}, true, }, }, @@ -485,10 +486,11 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceOIDCConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.OIDCConfigChangedType, + project.AggregateType, + []byte(`{ "oidcVersion": 0, "appId": "app-id", "redirectUris": ["redirect.one.ch", "redirect.two.ch"], @@ -507,32 +509,31 @@ func TestAppProjection_reduces(t *testing.T) { "skipNativeAppSuccessPage": true }`), - ), project.OIDCConfigChangedEventMapper), + ), project.OIDCConfigChangedEventMapper), }, reduce: (&appProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.apps5_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)", expectedArgs: []interface{}{ domain.OIDCVersionV1, - database.StringArray{"redirect.one.ch", "redirect.two.ch"}, - database.EnumArray[domain.OIDCResponseType]{1, 2}, - database.EnumArray[domain.OIDCGrantType]{1, 2}, + database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"}, + database.Array[domain.OIDCResponseType]{1, 2}, + database.Array[domain.OIDCGrantType]{1, 2}, domain.OIDCApplicationTypeNative, domain.OIDCAuthMethodTypeNone, - database.StringArray{"logout.one.ch", "logout.two.ch"}, + database.TextArray[string]{"logout.one.ch", "logout.two.ch"}, true, domain.OIDCTokenTypeJWT, true, true, true, 1 * time.Microsecond, - database.StringArray{"origin.one.ch", "origin.two.ch"}, + database.TextArray[string]{"origin.one.ch", "origin.two.ch"}, true, "app-id", "instance-id", @@ -554,19 +555,19 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceOIDCConfigChanged noop", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.OIDCConfigChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id" }`), - ), project.OIDCConfigChangedEventMapper), + ), project.OIDCConfigChangedEventMapper), }, reduce: (&appProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -575,20 +576,20 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project reduceOIDCConfigSecretChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigSecretChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.OIDCConfigSecretChangedType, + project.AggregateType, + []byte(`{ "appId": "app-id", "client_secret": {} }`), - ), project.OIDCConfigSecretChangedEventMapper), + ), project.OIDCConfigSecretChangedEventMapper), }, reduce: (&appProjection{}).reduceOIDCConfigSecretChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -615,17 +616,17 @@ func TestAppProjection_reduces(t *testing.T) { { name: "project.reduceOwnerRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, reduce: (&appProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/auth_request.go b/internal/query/projection/auth_request.go index b01ce175a..193f67c58 100644 --- a/internal/query/projection/auth_request.go +++ b/internal/query/projection/auth_request.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/authrequest" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -31,44 +31,46 @@ const ( AuthRequestColumnHintUserID = "hint_user_id" ) -type authRequestProjection struct { - crdb.StatementHandler +type authRequestProjection struct{} + +// Name implements handler.Projection. +func (*authRequestProjection) Name() string { + return AuthRequestsProjectionTable } -func newAuthRequestProjection(ctx context.Context, config crdb.StatementHandlerConfig) *authRequestProjection { - p := new(authRequestProjection) - config.ProjectionName = AuthRequestsProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(AuthRequestColumnID, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AuthRequestColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AuthRequestColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(AuthRequestColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnLoginClient, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnClientID, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnRedirectURI, crdb.ColumnTypeText), - crdb.NewColumn(AuthRequestColumnScope, crdb.ColumnTypeTextArray), - crdb.NewColumn(AuthRequestColumnPrompt, crdb.ColumnTypeEnumArray, crdb.Nullable()), - crdb.NewColumn(AuthRequestColumnUILocales, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(AuthRequestColumnMaxAge, crdb.ColumnTypeInt64, crdb.Nullable()), - crdb.NewColumn(AuthRequestColumnLoginHint, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(AuthRequestColumnHintUserID, crdb.ColumnTypeText, crdb.Nullable()), +func newAuthRequestProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(authRequestProjection)) +} + +func (*authRequestProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(AuthRequestColumnID, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(AuthRequestColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(AuthRequestColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(AuthRequestColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnLoginClient, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnClientID, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnRedirectURI, handler.ColumnTypeText), + handler.NewColumn(AuthRequestColumnScope, handler.ColumnTypeTextArray), + handler.NewColumn(AuthRequestColumnPrompt, handler.ColumnTypeEnumArray, handler.Nullable()), + handler.NewColumn(AuthRequestColumnUILocales, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(AuthRequestColumnMaxAge, handler.ColumnTypeInt64, handler.Nullable()), + handler.NewColumn(AuthRequestColumnLoginHint, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(AuthRequestColumnHintUserID, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(AuthRequestColumnInstanceID, AuthRequestColumnID), + handler.NewPrimaryKey(AuthRequestColumnInstanceID, AuthRequestColumnID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *authRequestProjection) reducers() []handler.AggregateReducer { +func (p *authRequestProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: authrequest.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: authrequest.AddedType, Reduce: p.reduceAuthRequestAdded, @@ -85,7 +87,7 @@ func (p *authRequestProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(AuthRequestColumnInstanceID), @@ -101,7 +103,7 @@ func (p *authRequestProjection) reduceAuthRequestAdded(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfwfa", "reduce.wrong.event.type %s", authrequest.AddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(AuthRequestColumnID, e.Aggregate().ID), @@ -132,7 +134,7 @@ func (p *authRequestProjection) reduceAuthRequestEnded(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASF3h", "reduce.wrong.event.type %s", []eventstore.EventType{authrequest.SucceededType, authrequest.FailedType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(AuthRequestColumnID, event.Aggregate().ID), diff --git a/internal/query/projection/auth_request_test.go b/internal/query/projection/auth_request_test.go index f0ce0651d..bda25fdf3 100644 --- a/internal/query/projection/auth_request_test.go +++ b/internal/query/projection/auth_request_test.go @@ -9,7 +9,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/authrequest" ) @@ -34,9 +34,8 @@ func TestAuthRequestProjection_reduces(t *testing.T) { }, reduce: (&authRequestProjection{}).reduceAuthRequestAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("auth_request"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("auth_request"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -74,9 +73,8 @@ func TestAuthRequestProjection_reduces(t *testing.T) { }, reduce: (&authRequestProjection{}).reduceAuthRequestEnded, want: wantReduce{ - aggregateType: eventstore.AggregateType("auth_request"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("auth_request"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -101,9 +99,8 @@ func TestAuthRequestProjection_reduces(t *testing.T) { }, reduce: (&authRequestProjection{}).reduceAuthRequestEnded, want: wantReduce{ - aggregateType: eventstore.AggregateType("auth_request"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("auth_request"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/authn_key.go b/internal/query/projection/authn_key.go index 789fb7edf..3d007dbca 100644 --- a/internal/query/projection/authn_key.go +++ b/internal/query/projection/authn_key.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -33,46 +33,47 @@ const ( AuthNKeyOwnerRemovedCol = "owner_removed" ) -type authNKeyProjection struct { - crdb.StatementHandler +type authNKeyProjection struct{} + +func newAuthNKeyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(authNKeyProjection)) } -func newAuthNKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *authNKeyProjection { - p := new(authNKeyProjection) - config.ProjectionName = AuthNKeyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(AuthNKeyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AuthNKeyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AuthNKeyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeyAggregateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(AuthNKeyObjectIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeyExpirationCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AuthNKeyIdentifierCol, crdb.ColumnTypeText), - crdb.NewColumn(AuthNKeyPublicKeyCol, crdb.ColumnTypeBytes), - crdb.NewColumn(AuthNKeyEnabledCol, crdb.ColumnTypeBool, crdb.Default(true)), - crdb.NewColumn(AuthNKeyTypeCol, crdb.ColumnTypeEnum, crdb.Default(0)), - crdb.NewColumn(AuthNKeyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*authNKeyProjection) Name() string { + return AuthNKeyTable +} + +func (*authNKeyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(AuthNKeyIDCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(AuthNKeyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(AuthNKeyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeyAggregateIDCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(AuthNKeyObjectIDCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeyExpirationCol, handler.ColumnTypeTimestamp), + handler.NewColumn(AuthNKeyIdentifierCol, handler.ColumnTypeText), + handler.NewColumn(AuthNKeyPublicKeyCol, handler.ColumnTypeBytes), + handler.NewColumn(AuthNKeyEnabledCol, handler.ColumnTypeBool, handler.Default(true)), + handler.NewColumn(AuthNKeyTypeCol, handler.ColumnTypeEnum, handler.Default(0)), + handler.NewColumn(AuthNKeyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(AuthNKeyInstanceIDCol, AuthNKeyIDCol), - crdb.WithIndex(crdb.NewIndex("enabled", []string{AuthNKeyEnabledCol})), - crdb.WithIndex(crdb.NewIndex("identifier", []string{AuthNKeyIdentifierCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{AuthNKeyOwnerRemovedCol})), + handler.NewPrimaryKey(AuthNKeyInstanceIDCol, AuthNKeyIDCol), + handler.WithIndex(handler.NewIndex("enabled", []string{AuthNKeyEnabledCol})), + handler.WithIndex(handler.NewIndex("identifier", []string{AuthNKeyIdentifierCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{AuthNKeyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *authNKeyProjection) reducers() []handler.AggregateReducer { +func (p *authNKeyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.ApplicationKeyAddedEventType, Reduce: p.reduceAuthNKeyAdded, @@ -101,7 +102,7 @@ func (p *authNKeyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.MachineKeyAddedEventType, Reduce: p.reduceAuthNKeyAdded, @@ -118,7 +119,7 @@ func (p *authNKeyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -127,7 +128,7 @@ func (p *authNKeyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(AuthNKeyInstanceIDCol), @@ -167,7 +168,7 @@ func (p *authNKeyProjection) reduceAuthNKeyAdded(event eventstore.Event) (*handl default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Dgb32", "reduce.wrong.event.type %v", []eventstore.EventType{project.ApplicationKeyAddedEventType, user.MachineKeyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &authNKeyEvent, []handler.Column{ handler.NewCol(AuthNKeyIDCol, authNKeyEvent.keyID), @@ -194,7 +195,7 @@ func (p *authNKeyProjection) reduceAuthNKeyEnabledChanged(event eventstore.Event switch e := event.(type) { case *project.APIConfigChangedEvent: if e.AuthMethodType == nil { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } appID = e.AppID enabled = *e.AuthMethodType == domain.APIAuthMethodTypePrivateKeyJWT @@ -202,7 +203,7 @@ func (p *authNKeyProjection) reduceAuthNKeyEnabledChanged(event eventstore.Event sequence = e.Sequence() case *project.OIDCConfigChangedEvent: if e.AuthMethodType == nil { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } appID = e.AppID enabled = *e.AuthMethodType == domain.OIDCAuthMethodTypePrivateKeyJWT @@ -211,7 +212,7 @@ func (p *authNKeyProjection) reduceAuthNKeyEnabledChanged(event eventstore.Event default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Dbrt1", "reduce.wrong.event.type %v", []eventstore.EventType{project.APIConfigChangedType, project.OIDCConfigChangedType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ handler.NewCol(AuthNKeyChangeDateCol, changeDate), @@ -241,7 +242,7 @@ func (p *authNKeyProjection) reduceAuthNKeyRemoved(event eventstore.Event) (*han default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-BGge42", "reduce.wrong.event.type %v", []eventstore.EventType{project.ApplicationKeyRemovedEventType, project.ApplicationRemovedType, project.ProjectRemovedType, user.MachineKeyRemovedEventType, user.UserRemovedType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ condition, @@ -256,7 +257,7 @@ func (p *authNKeyProjection) reduceOwnerRemoved(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Hyd1f", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(AuthNKeyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/authn_key_test.go b/internal/query/projection/authn_key_test.go index c1341fb64..bc016f375 100644 --- a/internal/query/projection/authn_key_test.go +++ b/internal/query/projection/authn_key_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -27,17 +26,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyAdded app", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationKeyAddedEventType), - project.AggregateType, - []byte(`{"applicationId": "appId", "clientId":"clientId","keyId": "keyId", "type": 1, "expirationDate": "2021-11-30T15:00:00Z", "publicKey": "cHVibGljS2V5"}`), - ), project.ApplicationKeyAddedEventMapper), + event: getEvent( + testEvent( + project.ApplicationKeyAddedEventType, + project.AggregateType, + []byte(`{"applicationId": "appId", "clientId":"clientId","keyId": "keyId", "type": 1, "expirationDate": "2021-11-30T15:00:00Z", "publicKey": "cHVibGljS2V5"}`), + ), project.ApplicationKeyAddedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -64,17 +63,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyAdded user", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineKeyAddedEventType), - user.AggregateType, - []byte(`{"keyId": "keyId", "type": 1, "expirationDate": "2021-11-30T15:00:00Z", "publicKey": "cHVibGljS2V5"}`), - ), user.MachineKeyAddedEventMapper), + event: getEvent( + testEvent( + user.MachineKeyAddedEventType, + user.AggregateType, + []byte(`{"keyId": "keyId", "type": 1, "expirationDate": "2021-11-30T15:00:00Z", "publicKey": "cHVibGljS2V5"}`), + ), user.MachineKeyAddedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -101,17 +100,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved app key", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationKeyRemovedEventType), - project.AggregateType, - []byte(`{"keyId": "keyId"}`), - ), project.ApplicationKeyRemovedEventMapper), + event: getEvent( + testEvent( + project.ApplicationKeyRemovedEventType, + project.AggregateType, + []byte(`{"keyId": "keyId"}`), + ), project.ApplicationKeyRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -128,17 +127,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged api no change", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId"}`), - ), project.APIConfigChangedEventMapper), + event: getEvent( + testEvent( + project.APIConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId"}`), + ), project.APIConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -147,17 +146,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged api config basic", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId", "authMethodType": 0}`), - ), project.APIConfigChangedEventMapper), + event: getEvent( + testEvent( + project.APIConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId", "authMethodType": 0}`), + ), project.APIConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -177,17 +176,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged api config jwt", args: args{ - event: getEvent(testEvent( - repository.EventType(project.APIConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId", "authMethodType": 1}`), - ), project.APIConfigChangedEventMapper), + event: getEvent( + testEvent( + project.APIConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId", "authMethodType": 1}`), + ), project.APIConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -207,17 +206,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved app key", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineKeyRemovedEventType), - user.AggregateType, - []byte(`{"keyId": "keyId"}`), - ), user.MachineKeyRemovedEventMapper), + event: getEvent( + testEvent( + user.MachineKeyRemovedEventType, + user.AggregateType, + []byte(`{"keyId": "keyId"}`), + ), user.MachineKeyRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -234,17 +233,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(AuthNKeyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -260,17 +259,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged oidc no change", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId"}`), - ), project.OIDCConfigChangedEventMapper), + event: getEvent( + testEvent( + project.OIDCConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId"}`), + ), project.OIDCConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -279,17 +278,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged oidc config basic", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId", "authMethodType": 0}`), - ), project.OIDCConfigChangedEventMapper), + event: getEvent( + testEvent( + project.OIDCConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId", "authMethodType": 0}`), + ), project.OIDCConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -309,17 +308,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyEnabledChanged oidc config jwt", args: args{ - event: getEvent(testEvent( - repository.EventType(project.OIDCConfigChangedType), - project.AggregateType, - []byte(`{"appId": "appId", "authMethodType": 3}`), - ), project.OIDCConfigChangedEventMapper), + event: getEvent( + testEvent( + project.OIDCConfigChangedType, + project.AggregateType, + []byte(`{"appId": "appId", "authMethodType": 3}`), + ), project.OIDCConfigChangedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyEnabledChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -339,17 +338,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved app key removed", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationKeyRemovedEventType), - project.AggregateType, - []byte(`{"keyId": "keyId"}`), - ), project.ApplicationKeyRemovedEventMapper), + event: getEvent( + testEvent( + project.ApplicationKeyRemovedEventType, + project.AggregateType, + []byte(`{"keyId": "keyId"}`), + ), project.ApplicationKeyRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -366,17 +365,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved app removed", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ApplicationRemovedType), - project.AggregateType, - []byte(`{"appId": "appId"}`), - ), project.ApplicationRemovedEventMapper), + event: getEvent( + testEvent( + project.ApplicationRemovedType, + project.AggregateType, + []byte(`{"appId": "appId"}`), + ), project.ApplicationRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -393,17 +392,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved project removed", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - nil, - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + nil, + ), project.ProjectRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -420,17 +419,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved machine key removed", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineKeyRemovedEventType), - user.AggregateType, - []byte(`{"keyId": "keyId"}`), - ), user.MachineKeyRemovedEventMapper), + event: getEvent( + testEvent( + user.MachineKeyRemovedEventType, + user.AggregateType, + []byte(`{"keyId": "keyId"}`), + ), user.MachineKeyRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -447,17 +446,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceAuthNKeyRemoved user removed", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{"keyId": "keyId"}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{"keyId": "keyId"}`), + ), user.UserRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceAuthNKeyRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -474,17 +473,17 @@ func TestAuthNKeyProjection_reduces(t *testing.T) { { name: "reduceOwnerRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, reduce: (&authNKeyProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/config.go b/internal/query/projection/config.go index 355913639..d1a79f1c2 100644 --- a/internal/query/projection/config.go +++ b/internal/query/projection/config.go @@ -7,18 +7,20 @@ import ( type Config struct { RequeueEvery time.Duration RetryFailedAfter time.Duration - MaxFailureCount uint + MaxFailureCount uint8 ConcurrentInstances uint BulkLimit uint64 Customizations map[string]CustomConfig HandleActiveInstances time.Duration + TransactionDuration time.Duration } type CustomConfig struct { RequeueEvery *time.Duration RetryFailedAfter *time.Duration - MaxFailureCount *uint + MaxFailureCount *uint8 ConcurrentInstances *uint - BulkLimit *uint64 + BulkLimit *uint16 HandleActiveInstances *time.Duration + TransactionDuration *time.Duration } diff --git a/internal/query/projection/custom_text.go b/internal/query/projection/custom_text.go index b2e3650c4..a406f4e50 100644 --- a/internal/query/projection/custom_text.go +++ b/internal/query/projection/custom_text.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -28,41 +28,42 @@ const ( CustomTextOwnerRemovedCol = "owner_removed" ) -type customTextProjection struct { - crdb.StatementHandler +type customTextProjection struct{} + +func newCustomTextProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(customTextProjection)) } -func newCustomTextProjection(ctx context.Context, config crdb.StatementHandlerConfig) *customTextProjection { - p := new(customTextProjection) - config.ProjectionName = CustomTextTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(CustomTextAggregateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(CustomTextChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(CustomTextSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(CustomTextIsDefaultCol, crdb.ColumnTypeBool), - crdb.NewColumn(CustomTextTemplateCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextLanguageCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextKeyCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextTextCol, crdb.ColumnTypeText), - crdb.NewColumn(CustomTextOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*customTextProjection) Name() string { + return CustomTextTable +} + +func (*customTextProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(CustomTextAggregateIDCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(CustomTextChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(CustomTextSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(CustomTextIsDefaultCol, handler.ColumnTypeBool), + handler.NewColumn(CustomTextTemplateCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextLanguageCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextKeyCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextTextCol, handler.ColumnTypeText), + handler.NewColumn(CustomTextOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(CustomTextInstanceIDCol, CustomTextAggregateIDCol, CustomTextTemplateCol, CustomTextKeyCol, CustomTextLanguageCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{CustomTextOwnerRemovedCol})), + handler.NewPrimaryKey(CustomTextInstanceIDCol, CustomTextAggregateIDCol, CustomTextTemplateCol, CustomTextKeyCol, CustomTextLanguageCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{CustomTextOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *customTextProjection) reducers() []handler.AggregateReducer { +func (p *customTextProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.CustomTextSetEventType, Reduce: p.reduceSet, @@ -83,7 +84,7 @@ func (p *customTextProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.CustomTextSetEventType, Reduce: p.reduceSet, @@ -118,7 +119,7 @@ func (p *customTextProjection) reduceSet(event eventstore.Event) (*handler.State default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-KKfw4", "reduce.wrong.event.type %v", []eventstore.EventType{org.CustomTextSetEventType, instance.CustomTextSetEventType}) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( &customTextEvent, []handler.Column{ handler.NewCol(CustomTextInstanceIDCol, nil), @@ -151,7 +152,7 @@ func (p *customTextProjection) reduceRemoved(event eventstore.Event) (*handler.S default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-n9wJg", "reduce.wrong.event.type %v", []eventstore.EventType{org.CustomTextRemovedEventType, instance.CustomTextRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &customTextEvent, []handler.Condition{ handler.NewCond(CustomTextAggregateIDCol, customTextEvent.Aggregate().ID), @@ -172,7 +173,7 @@ func (p *customTextProjection) reduceTemplateRemoved(event eventstore.Event) (*h default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-29iPf", "reduce.wrong.event.type %v", []eventstore.EventType{org.CustomTextTemplateRemovedEventType, instance.CustomTextTemplateRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &customTextEvent, []handler.Condition{ handler.NewCond(CustomTextAggregateIDCol, customTextEvent.Aggregate().ID), @@ -188,7 +189,7 @@ func (p *customTextProjection) reduceOwnerRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-V2T3z", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(CustomTextInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/custom_text_test.go b/internal/query/projection/custom_text_test.go index f6889cf40..951e236cf 100644 --- a/internal/query/projection/custom_text_test.go +++ b/internal/query/projection/custom_text_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -24,22 +23,22 @@ func TestCustomTextProjection_reduces(t *testing.T) { { name: "org reduceSet", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&customTextProjection{}).reduceSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -65,20 +64,20 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&customTextProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -99,20 +98,20 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "org reduceTemplateRemoved", reduce: (&customTextProjection{}).reduceTemplateRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextTemplateRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextTemplateRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode" }`), - ), org.CustomTextTemplateRemovedEventMapper), + ), org.CustomTextTemplateRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -131,17 +130,17 @@ func TestCustomTextProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(CustomTextInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -158,21 +157,21 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&customTextProjection{}).reduceSet, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.CustomTextSetEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.CustomTextSetEventType, + instance.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode", "text": "Test" }`), - ), instance.CustomTextSetEventMapper), + ), instance.CustomTextSetEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -198,20 +197,20 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "instance reduceRemoved", reduce: (&customTextProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.CustomTextTemplateRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.CustomTextTemplateRemovedEventType, + instance.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode" }`), - ), instance.CustomTextRemovedEventMapper), + ), instance.CustomTextRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -232,20 +231,20 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "instance reduceTemplateRemoved", reduce: (&customTextProjection{}).reduceTemplateRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.CustomTextTemplateRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.CustomTextTemplateRemovedEventType, + instance.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode" }`), - ), instance.CustomTextTemplateRemovedEventMapper), + ), instance.CustomTextTemplateRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -265,16 +264,16 @@ func TestCustomTextProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&customTextProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/debug_notification.go b/internal/query/projection/debug_notification.go index 9eff62ee8..7d553ed0b 100644 --- a/internal/query/projection/debug_notification.go +++ b/internal/query/projection/debug_notification.go @@ -8,8 +8,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -27,38 +27,39 @@ const ( DebugNotificationProviderCompactCol = "compact" ) -type debugNotificationProviderProjection struct { - crdb.StatementHandler +type debugNotificationProviderProjection struct{} + +func newDebugNotificationProviderProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(debugNotificationProviderProjection)) } -func newDebugNotificationProviderProjection(ctx context.Context, config crdb.StatementHandlerConfig) *debugNotificationProviderProjection { - p := &debugNotificationProviderProjection{} - config.ProjectionName = DebugNotificationProviderTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(DebugNotificationProviderAggIDCol, crdb.ColumnTypeText), - crdb.NewColumn(DebugNotificationProviderCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DebugNotificationProviderChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DebugNotificationProviderSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(DebugNotificationProviderResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(DebugNotificationProviderInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(DebugNotificationProviderStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(DebugNotificationProviderTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(DebugNotificationProviderCompactCol, crdb.ColumnTypeBool), +func (*debugNotificationProviderProjection) Name() string { + return DebugNotificationProviderTable +} + +func (*debugNotificationProviderProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(DebugNotificationProviderAggIDCol, handler.ColumnTypeText), + handler.NewColumn(DebugNotificationProviderCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(DebugNotificationProviderChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(DebugNotificationProviderSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(DebugNotificationProviderResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(DebugNotificationProviderInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(DebugNotificationProviderStateCol, handler.ColumnTypeEnum), + handler.NewColumn(DebugNotificationProviderTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(DebugNotificationProviderCompactCol, handler.ColumnTypeBool), }, - crdb.NewPrimaryKey(DebugNotificationProviderInstanceIDCol, DebugNotificationProviderAggIDCol, DebugNotificationProviderTypeCol), + handler.NewPrimaryKey(DebugNotificationProviderInstanceIDCol, DebugNotificationProviderAggIDCol, DebugNotificationProviderTypeCol), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *debugNotificationProviderProjection) reducers() []handler.AggregateReducer { +func (p *debugNotificationProviderProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.DebugNotificationProviderFileAddedEventType, Reduce: p.reduceDebugNotificationProviderAdded, @@ -106,7 +107,7 @@ func (p *debugNotificationProviderProjection) reduceDebugNotificationProviderAdd return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-pYPxS", "reduce.wrong.event.type %v", []eventstore.EventType{instance.DebugNotificationProviderFileAddedEventType, instance.DebugNotificationProviderLogAddedEventType}) } - return crdb.NewCreateStatement(&providerEvent, []handler.Column{ + return handler.NewCreateStatement(&providerEvent, []handler.Column{ handler.NewCol(DebugNotificationProviderAggIDCol, providerEvent.Aggregate().ID), handler.NewCol(DebugNotificationProviderCreationDateCol, providerEvent.CreationDate()), handler.NewCol(DebugNotificationProviderChangeDateCol, providerEvent.CreationDate()), @@ -141,7 +142,7 @@ func (p *debugNotificationProviderProjection) reduceDebugNotificationProviderCha cols = append(cols, handler.NewCol(DebugNotificationProviderCompactCol, *providerEvent.Compact)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &providerEvent, cols, []handler.Condition{ @@ -166,7 +167,7 @@ func (p *debugNotificationProviderProjection) reduceDebugNotificationProviderRem return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-dow9f", "reduce.wrong.event.type %v", []eventstore.EventType{instance.DebugNotificationProviderFileRemovedEventType, instance.DebugNotificationProviderLogRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &providerEvent, []handler.Condition{ handler.NewCond(DebugNotificationProviderAggIDCol, providerEvent.Aggregate().ID), diff --git a/internal/query/projection/debug_notification_provider_test.go b/internal/query/projection/debug_notification_provider_test.go index ec22287e5..cbe943a7a 100644 --- a/internal/query/projection/debug_notification_provider_test.go +++ b/internal/query/projection/debug_notification_provider_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -25,18 +24,18 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderFileAdded", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderFileAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DebugNotificationProviderFileAddedEventType, + instance.AggregateType, + []byte(`{ "compact": true }`), - ), instance.DebugNotificationProviderFileAddedEventMapper), + ), instance.DebugNotificationProviderFileAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -61,18 +60,18 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderFileChanged", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderFileChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DebugNotificationProviderFileChangedEventType, + instance.AggregateType, + []byte(`{ "compact": true }`), - ), instance.DebugNotificationProviderFileChangedEventMapper), + ), instance.DebugNotificationProviderFileChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -94,16 +93,16 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderFileRemoved", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderFileRemovedEventType), - instance.AggregateType, - nil, - ), instance.DebugNotificationProviderFileRemovedEventMapper), + event: getEvent( + testEvent( + instance.DebugNotificationProviderFileRemovedEventType, + instance.AggregateType, + nil, + ), instance.DebugNotificationProviderFileRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -122,18 +121,18 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderLogAdded", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderLogAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DebugNotificationProviderLogAddedEventType, + instance.AggregateType, + []byte(`{ "compact": true }`), - ), instance.DebugNotificationProviderLogAddedEventMapper), + ), instance.DebugNotificationProviderLogAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -158,18 +157,18 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderLogChanged", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderLogChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DebugNotificationProviderLogChangedEventType, + instance.AggregateType, + []byte(`{ "compact": true }`), - ), instance.DebugNotificationProviderLogChangedEventMapper), + ), instance.DebugNotificationProviderLogChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -191,16 +190,16 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { name: "instance reduceNotificationProviderLogRemoved", reduce: (&debugNotificationProviderProjection{}).reduceDebugNotificationProviderRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DebugNotificationProviderLogRemovedEventType), - instance.AggregateType, - nil, - ), instance.DebugNotificationProviderLogRemovedEventMapper), + event: getEvent( + testEvent( + instance.DebugNotificationProviderLogRemovedEventType, + instance.AggregateType, + nil, + ), instance.DebugNotificationProviderLogRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -218,17 +217,17 @@ func TestDebugNotificationProviderProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(DebugNotificationProviderInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/device_auth.go b/internal/query/projection/device_auth.go index c678dbd30..f0232192e 100644 --- a/internal/query/projection/device_auth.go +++ b/internal/query/projection/device_auth.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/deviceauth" ) @@ -29,44 +29,44 @@ const ( DeviceAuthColumnInstanceID = "instance_id" ) -type deviceAuthProjection struct { - crdb.StatementHandler +type deviceAuthProjection struct{} + +func newDeviceAuthProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(deviceAuthProjection)) } -func newDeviceAuthProjection(ctx context.Context, config crdb.StatementHandlerConfig) *deviceAuthProjection { - p := new(deviceAuthProjection) - config.ProjectionName = DeviceAuthProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(DeviceAuthColumnID, crdb.ColumnTypeText), - crdb.NewColumn(DeviceAuthColumnClientID, crdb.ColumnTypeText), - crdb.NewColumn(DeviceAuthColumnDeviceCode, crdb.ColumnTypeText), - crdb.NewColumn(DeviceAuthColumnUserCode, crdb.ColumnTypeText), - crdb.NewColumn(DeviceAuthColumnExpires, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DeviceAuthColumnScopes, crdb.ColumnTypeTextArray), - crdb.NewColumn(DeviceAuthColumnState, crdb.ColumnTypeEnum, crdb.Default(domain.DeviceAuthStateInitiated)), - crdb.NewColumn(DeviceAuthColumnSubject, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(DeviceAuthColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DeviceAuthColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DeviceAuthColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(DeviceAuthColumnInstanceID, crdb.ColumnTypeText), +func (*deviceAuthProjection) Name() string { + return DeviceAuthProjectionTable +} + +func (*deviceAuthProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(DeviceAuthColumnID, handler.ColumnTypeText), + handler.NewColumn(DeviceAuthColumnClientID, handler.ColumnTypeText), + handler.NewColumn(DeviceAuthColumnDeviceCode, handler.ColumnTypeText), + handler.NewColumn(DeviceAuthColumnUserCode, handler.ColumnTypeText), + handler.NewColumn(DeviceAuthColumnExpires, handler.ColumnTypeTimestamp), + handler.NewColumn(DeviceAuthColumnScopes, handler.ColumnTypeTextArray), + handler.NewColumn(DeviceAuthColumnState, handler.ColumnTypeEnum, handler.Default(domain.DeviceAuthStateInitiated)), + handler.NewColumn(DeviceAuthColumnSubject, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(DeviceAuthColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(DeviceAuthColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(DeviceAuthColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(DeviceAuthColumnInstanceID, handler.ColumnTypeText), }, - crdb.NewPrimaryKey(DeviceAuthColumnInstanceID, DeviceAuthColumnID), - crdb.WithIndex(crdb.NewIndex("user_code", []string{DeviceAuthColumnInstanceID, DeviceAuthColumnUserCode})), - crdb.WithIndex(crdb.NewIndex("device_code", []string{DeviceAuthColumnInstanceID, DeviceAuthColumnClientID, DeviceAuthColumnDeviceCode})), + handler.NewPrimaryKey(DeviceAuthColumnInstanceID, DeviceAuthColumnID), + handler.WithIndex(handler.NewIndex("user_code", []string{DeviceAuthColumnInstanceID, DeviceAuthColumnUserCode})), + handler.WithIndex(handler.NewIndex("device_code", []string{DeviceAuthColumnInstanceID, DeviceAuthColumnClientID, DeviceAuthColumnDeviceCode})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *deviceAuthProjection) reducers() []handler.AggregateReducer { +func (p *deviceAuthProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: deviceauth.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: deviceauth.AddedEventType, Reduce: p.reduceAdded, @@ -93,7 +93,7 @@ func (p *deviceAuthProjection) reduceAdded(event eventstore.Event) (*handler.Sta if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-chu6O", "reduce.wrong.event.type %T != %s", event, deviceauth.AddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(DeviceAuthColumnID, e.Aggregate().ID), @@ -115,7 +115,7 @@ func (p *deviceAuthProjection) reduceAppoved(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-kei0A", "reduce.wrong.event.type %T != %s", event, deviceauth.ApprovedEventType) } - return crdb.NewUpdateStatement(e, + return handler.NewUpdateStatement(e, []handler.Column{ handler.NewCol(DeviceAuthColumnState, domain.DeviceAuthStateApproved), handler.NewCol(DeviceAuthColumnSubject, e.Subject), @@ -134,7 +134,7 @@ func (p *deviceAuthProjection) reduceCanceled(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-eeS8d", "reduce.wrong.event.type %T != %s", event, deviceauth.CanceledEventType) } - return crdb.NewUpdateStatement(e, + return handler.NewUpdateStatement(e, []handler.Column{ handler.NewCol(DeviceAuthColumnState, e.Reason.State()), handler.NewCol(DeviceAuthColumnChangeDate, e.CreationDate()), @@ -152,7 +152,7 @@ func (p *deviceAuthProjection) reduceRemoved(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-AJi1u", "reduce.wrong.event.type %T != %s", event, deviceauth.RemovedEventType) } - return crdb.NewDeleteStatement(e, + return handler.NewDeleteStatement(e, []handler.Condition{ handler.NewCond(DeviceAuthColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(DeviceAuthColumnID, e.Aggregate().ID), diff --git a/internal/query/projection/domain_policy.go b/internal/query/projection/domain_policy.go index 97896edb9..b11fbf260 100644 --- a/internal/query/projection/domain_policy.go +++ b/internal/query/projection/domain_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -30,42 +30,43 @@ const ( DomainPolicyOwnerRemovedCol = "owner_removed" ) -type domainPolicyProjection struct { - crdb.StatementHandler +type domainPolicyProjection struct{} + +func newDomainPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(domainPolicyProjection)) } -func newDomainPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *domainPolicyProjection { - p := new(domainPolicyProjection) - config.ProjectionName = DomainPolicyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(DomainPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(DomainPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DomainPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(DomainPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(DomainPolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(DomainPolicyUserLoginMustBeDomainCol, crdb.ColumnTypeBool), - crdb.NewColumn(DomainPolicyValidateOrgDomainsCol, crdb.ColumnTypeBool), - crdb.NewColumn(DomainPolicySMTPSenderAddressMatchesInstanceDomainCol, crdb.ColumnTypeBool), - crdb.NewColumn(DomainPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(DomainPolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(DomainPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(DomainPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*domainPolicyProjection) Name() string { + return DomainPolicyTable +} + +func (*domainPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(DomainPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(DomainPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(DomainPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(DomainPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(DomainPolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(DomainPolicyUserLoginMustBeDomainCol, handler.ColumnTypeBool), + handler.NewColumn(DomainPolicyValidateOrgDomainsCol, handler.ColumnTypeBool), + handler.NewColumn(DomainPolicySMTPSenderAddressMatchesInstanceDomainCol, handler.ColumnTypeBool), + handler.NewColumn(DomainPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(DomainPolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(DomainPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(DomainPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(DomainPolicyInstanceIDCol, DomainPolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{DomainPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(DomainPolicyInstanceIDCol, DomainPolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{DomainPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *domainPolicyProjection) reducers() []handler.AggregateReducer { +func (p *domainPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.DomainPolicyAddedEventType, Reduce: p.reduceAdded, @@ -86,7 +87,7 @@ func (p *domainPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.DomainPolicyAddedEventType, Reduce: p.reduceAdded, @@ -117,7 +118,7 @@ func (p *domainPolicyProjection) reduceAdded(event eventstore.Event) (*handler.S default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-CSE7A", "reduce.wrong.event.type %v", []eventstore.EventType{org.DomainPolicyAddedEventType, instance.DomainPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(DomainPolicyCreationDateCol, policyEvent.CreationDate()), @@ -157,7 +158,7 @@ func (p *domainPolicyProjection) reduceChanged(event eventstore.Event) (*handler if policyEvent.SMTPSenderAddressMatchesInstanceDomain != nil { cols = append(cols, handler.NewCol(DomainPolicySMTPSenderAddressMatchesInstanceDomainCol, *policyEvent.SMTPSenderAddressMatchesInstanceDomain)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -171,7 +172,7 @@ func (p *domainPolicyProjection) reduceRemoved(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-JAENd", "reduce.wrong.event.type %s", org.DomainPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(DomainPolicyIDCol, policyEvent.Aggregate().ID), @@ -185,7 +186,7 @@ func (p *domainPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-JYD2K", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(DomainPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/domain_policy_test.go b/internal/query/projection/domain_policy_test.go index 7aa64999e..0df5f786d 100644 --- a/internal/query/projection/domain_policy_test.go +++ b/internal/query/projection/domain_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,21 +24,21 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.DomainPolicyAddedEventType, + org.AggregateType, + []byte(`{ "userLoginMustBeDomain": true, "validateOrgDomains": true, "smtpSenderAddressMatchesInstanceDomain": true }`), - ), org.DomainPolicyAddedEventMapper), + ), org.DomainPolicyAddedEventMapper), }, reduce: (&domainPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -66,20 +65,20 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (&domainPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.DomainPolicyChangedEventType, + org.AggregateType, + []byte(`{ "userLoginMustBeDomain": true, "validateOrgDomains": true, "smtpSenderAddressMatchesInstanceDomain": true }`), - ), org.DomainPolicyChangedEventMapper), + ), org.DomainPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -102,16 +101,16 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&domainPolicyProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.DomainPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.DomainPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.DomainPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -128,17 +127,17 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(DomainPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -155,20 +154,20 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&domainPolicyProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DomainPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DomainPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "userLoginMustBeDomain": true, "validateOrgDomains": true, "smtpSenderAddressMatchesInstanceDomain": true }`), - ), instance.DomainPolicyAddedEventMapper), + ), instance.DomainPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -195,20 +194,20 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (&domainPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DomainPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DomainPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "userLoginMustBeDomain": true, "validateOrgDomains": true, "smtpSenderAddressMatchesInstanceDomain": true }`), - ), instance.DomainPolicyChangedEventMapper), + ), instance.DomainPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -231,16 +230,16 @@ func TestDomainPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&domainPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/event_test.go b/internal/query/projection/event_test.go index 40dd2234e..4998629cf 100644 --- a/internal/query/projection/event_test.go +++ b/internal/query/projection/event_test.go @@ -6,44 +6,41 @@ import ( "time" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/eventstore/repository" ) func testEvent( - eventType repository.EventType, - aggregateType repository.AggregateType, + eventType eventstore.EventType, + aggregateType eventstore.AggregateType, data []byte, ) *repository.Event { return timedTestEvent(eventType, aggregateType, data, time.Now()) } func toSystemEvent(event *repository.Event) *repository.Event { - event.EditorService = "SYSTEM" + event.EditorUser = "SYSTEM" return event } func timedTestEvent( - eventType repository.EventType, - aggregateType repository.AggregateType, + eventType eventstore.EventType, + aggregateType eventstore.AggregateType, data []byte, creationDate time.Time, ) *repository.Event { return &repository.Event{ - Sequence: 15, - PreviousAggregateSequence: 10, - PreviousAggregateTypeSequence: 10, - CreationDate: creationDate, - Type: eventType, - AggregateType: aggregateType, - Data: data, - Version: "v1", - AggregateID: "agg-id", - ResourceOwner: sql.NullString{String: "ro-id", Valid: true}, - InstanceID: "instance-id", - ID: "event-id", - EditorService: "editor-svc", - EditorUser: "editor-user", + Seq: 15, + CreationDate: creationDate, + Typ: eventType, + AggregateType: aggregateType, + Data: data, + Version: "v1", + AggregateID: "agg-id", + ResourceOwner: sql.NullString{String: "ro-id", Valid: true}, + InstanceID: "instance-id", + ID: "event-id", + EditorUser: "editor-user", } } @@ -51,7 +48,7 @@ func baseEvent(*testing.T) eventstore.Event { return &eventstore.BaseEvent{} } -func getEvent(event *repository.Event, mapper func(*repository.Event) (eventstore.Event, error)) func(t *testing.T) eventstore.Event { +func getEvent(event *repository.Event, mapper func(eventstore.Event) (eventstore.Event, error)) func(t *testing.T) eventstore.Event { return func(t *testing.T) eventstore.Event { e, err := mapper(event) if err != nil { @@ -62,11 +59,10 @@ func getEvent(event *repository.Event, mapper func(*repository.Event) (eventstor } type wantReduce struct { - aggregateType eventstore.AggregateType - sequence uint64 - previousSequence uint64 - executer *testExecuter - err func(error) bool + aggregateType eventstore.AggregateType + sequence uint64 + executer *testExecuter + err func(error) bool } func assertReduce(t *testing.T, stmt *handler.Statement, err error, projection string, want wantReduce) { @@ -82,10 +78,6 @@ func assertReduce(t *testing.T, stmt *handler.Statement, err error, projection s t.Errorf("wrong aggregate type: want: %q got: %q", want.aggregateType, stmt.AggregateType) } - if stmt.PreviousSequence != want.previousSequence { - t.Errorf("wrong previous sequence: want: %d got: %d", want.previousSequence, stmt.PreviousSequence) - } - if stmt.Sequence != want.sequence { t.Errorf("wrong sequence: want: %d got: %d", want.sequence, stmt.Sequence) } diff --git a/internal/query/projection/eventstore_mock_test.go b/internal/query/projection/eventstore_mock_test.go new file mode 100644 index 000000000..672b19ae3 --- /dev/null +++ b/internal/query/projection/eventstore_mock_test.go @@ -0,0 +1,44 @@ +package projection + +import ( + "context" + "time" + + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" +) + +var _ handler.EventStore = (*mockEventStore)(nil) + +type mockEventStore struct { + instanceIDsResponse [][]string + instanceIDCounter int + filterResponse [][]eventstore.Event + filterCounter int + pushResponse [][]eventstore.Event + pushCounter int +} + +func newMockEventStore() *mockEventStore { + return new(mockEventStore) +} + +func (m *mockEventStore) appendFilterResponse(events []eventstore.Event) *mockEventStore { + m.filterResponse = append(m.filterResponse, events) + return m +} + +func (m *mockEventStore) InstanceIDs(ctx context.Context, _ time.Duration, _ bool, query *eventstore.SearchQueryBuilder) ([]string, error) { + m.instanceIDCounter++ + return m.instanceIDsResponse[m.instanceIDCounter-1], nil +} + +func (m *mockEventStore) Filter(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + m.filterCounter++ + return m.filterResponse[m.filterCounter-1], nil +} + +func (m *mockEventStore) Push(ctx context.Context, cmds ...eventstore.Command) ([]eventstore.Event, error) { + m.pushCounter++ + return m.pushResponse[m.pushCounter-1], nil +} diff --git a/internal/query/projection/executer_test.go b/internal/query/projection/executer_test.go index 565700b89..8dca87a38 100644 --- a/internal/query/projection/executer_test.go +++ b/internal/query/projection/executer_test.go @@ -25,6 +25,10 @@ type execution struct { type anyArg struct{} func (e *testExecuter) Exec(stmt string, args ...interface{}) (sql.Result, error) { + if stmt == "SAVEPOINT stmt_exec" || stmt == "RELEASE SAVEPOINT stmt_exec" { + return nil, nil + } + if e.execIdx >= len(e.executions) { return nil, errors.ThrowInternal(nil, "PROJE-8TNoE", "too many executions") } diff --git a/internal/query/projection/flow.go b/internal/query/projection/flow.go index 62c6cb158..5375ba858 100644 --- a/internal/query/projection/flow.go +++ b/internal/query/projection/flow.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -24,39 +24,40 @@ const ( FlowOwnerRemovedCol = "owner_removed" ) -type flowProjection struct { - crdb.StatementHandler +type flowProjection struct{} + +func newFlowProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(flowProjection)) } -func newFlowProjection(ctx context.Context, config crdb.StatementHandlerConfig) *flowProjection { - p := new(flowProjection) - config.ProjectionName = FlowTriggerTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(FlowTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(FlowChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(FlowSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(FlowTriggerTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(FlowResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(FlowInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(FlowActionTriggerSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(FlowActionIDCol, crdb.ColumnTypeText), - crdb.NewColumn(FlowOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*flowProjection) Name() string { + return FlowTriggerTable +} + +func (*flowProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(FlowTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(FlowChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(FlowSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(FlowTriggerTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(FlowResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(FlowInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(FlowActionTriggerSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(FlowActionIDCol, handler.ColumnTypeText), + handler.NewColumn(FlowOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(FlowInstanceIDCol, FlowTypeCol, FlowTriggerTypeCol, FlowResourceOwnerCol, FlowActionIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{FlowOwnerRemovedCol})), + handler.NewPrimaryKey(FlowInstanceIDCol, FlowTypeCol, FlowTriggerTypeCol, FlowResourceOwnerCol, FlowActionIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{FlowOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *flowProjection) reducers() []handler.AggregateReducer { +func (p *flowProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.TriggerActionsSetEventType, Reduce: p.reduceTriggerActionsSetEventType, @@ -73,7 +74,7 @@ func (p *flowProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(FlowInstanceIDCol), @@ -88,8 +89,8 @@ func (p *flowProjection) reduceTriggerActionsSetEventType(event eventstore.Event if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-uYq4r", "reduce.wrong.event.type %s", org.TriggerActionsSetEventType) } - stmts := make([]func(reader eventstore.Event) crdb.Exec, len(e.ActionIDs)+1) - stmts[0] = crdb.AddDeleteStatement( + stmts := make([]func(reader eventstore.Event) handler.Exec, len(e.ActionIDs)+1) + stmts[0] = handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(FlowTypeCol, e.FlowType), handler.NewCond(FlowTriggerTypeCol, e.TriggerType), @@ -98,7 +99,7 @@ func (p *flowProjection) reduceTriggerActionsSetEventType(event eventstore.Event }, ) for i, id := range e.ActionIDs { - stmts[i+1] = crdb.AddCreateStatement( + stmts[i+1] = handler.AddCreateStatement( []handler.Column{ handler.NewCol(FlowResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCol(FlowInstanceIDCol, e.Aggregate().InstanceID), @@ -111,7 +112,7 @@ func (p *flowProjection) reduceTriggerActionsSetEventType(event eventstore.Event }, ) } - return crdb.NewMultiStatement(e, stmts...), nil + return handler.NewMultiStatement(e, stmts...), nil } func (p *flowProjection) reduceFlowClearedEventType(event eventstore.Event) (*handler.Statement, error) { @@ -119,7 +120,7 @@ func (p *flowProjection) reduceFlowClearedEventType(event eventstore.Event) (*ha if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-uYq4r", "reduce.wrong.event.type %s", org.FlowClearedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(FlowTypeCol, e.FlowType), @@ -135,7 +136,7 @@ func (p *flowProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Yd7WC", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(FlowInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/flow_test.go b/internal/query/projection/flow_test.go index 49b1b8d49..ca85c312c 100644 --- a/internal/query/projection/flow_test.go +++ b/internal/query/projection/flow_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,17 +24,17 @@ func TestFlowProjection_reduces(t *testing.T) { { name: "reduceTriggerActionsSetEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.TriggerActionsSetEventType), - org.AggregateType, - []byte(`{"flowType": 1, "triggerType": 1, "actionIDs": ["id1", "id2"]}`), - ), org.TriggerActionsSetEventMapper), + event: getEvent( + testEvent( + org.TriggerActionsSetEventType, + org.AggregateType, + []byte(`{"flowType": 1, "triggerType": 1, "actionIDs": ["id1", "id2"]}`), + ), org.TriggerActionsSetEventMapper), }, reduce: (&flowProjection{}).reduceTriggerActionsSetEventType, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -80,17 +79,17 @@ func TestFlowProjection_reduces(t *testing.T) { { name: "reduceFlowClearedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.FlowClearedEventType), - org.AggregateType, - []byte(`{"flowType": 1}`), - ), org.FlowClearedEventMapper), + event: getEvent( + testEvent( + org.FlowClearedEventType, + org.AggregateType, + []byte(`{"flowType": 1}`), + ), org.FlowClearedEventMapper), }, reduce: (&flowProjection{}).reduceFlowClearedEventType, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -109,16 +108,16 @@ func TestFlowProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&flowProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -135,17 +134,17 @@ func TestFlowProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(FlowInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/idp.go b/internal/query/projection/idp.go index a355c7adc..db5cc4319 100644 --- a/internal/query/projection/idp.go +++ b/internal/query/projection/idp.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" @@ -55,72 +55,73 @@ const ( JWTConfigEndpointCol = "endpoint" ) -type idpProjection struct { - crdb.StatementHandler +type idpProjection struct{} + +func newIDPProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(idpProjection)) } -func newIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *idpProjection { - p := new(idpProjection) - config.ProjectionName = IDPTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(IDPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(IDPResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPNameCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPStylingTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPOwnerTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPAutoRegisterCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum, crdb.Nullable()), - crdb.NewColumn(IDPOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*idpProjection) Name() string { + return IDPTable +} + +func (*idpProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(IDPIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(IDPResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(IDPInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPStateCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPNameCol, handler.ColumnTypeText), + handler.NewColumn(IDPStylingTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPOwnerTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPAutoRegisterCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(IDPTypeCol, handler.ColumnTypeEnum, handler.Nullable()), + handler.NewColumn(IDPOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(IDPInstanceIDCol, IDPIDCol), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{IDPResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{IDPOwnerRemovedCol})), + handler.NewPrimaryKey(IDPInstanceIDCol, IDPIDCol), + handler.WithIndex(handler.NewIndex("resource_owner", []string{IDPResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{IDPOwnerRemovedCol})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(OIDCConfigIDPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCConfigInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCConfigClientIDCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(OIDCConfigClientSecretCol, crdb.ColumnTypeJSONB, crdb.Nullable()), - crdb.NewColumn(OIDCConfigIssuerCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(OIDCConfigScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(OIDCConfigDisplayNameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()), - crdb.NewColumn(OIDCConfigUsernameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()), - crdb.NewColumn(OIDCConfigAuthorizationEndpointCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(OIDCConfigTokenEndpointCol, crdb.ColumnTypeText, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(OIDCConfigIDPIDCol, handler.ColumnTypeText), + handler.NewColumn(OIDCConfigInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(OIDCConfigClientIDCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(OIDCConfigClientSecretCol, handler.ColumnTypeJSONB, handler.Nullable()), + handler.NewColumn(OIDCConfigIssuerCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(OIDCConfigScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(OIDCConfigDisplayNameMappingCol, handler.ColumnTypeEnum, handler.Nullable()), + handler.NewColumn(OIDCConfigUsernameMappingCol, handler.ColumnTypeEnum, handler.Nullable()), + handler.NewColumn(OIDCConfigAuthorizationEndpointCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(OIDCConfigTokenEndpointCol, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(OIDCConfigInstanceIDCol, OIDCConfigIDPIDCol), + handler.NewPrimaryKey(OIDCConfigInstanceIDCol, OIDCConfigIDPIDCol), IDPOIDCSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(JWTConfigIDPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTConfigInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTConfigIssuerCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(JWTConfigKeysEndpointCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(JWTConfigHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(JWTConfigEndpointCol, crdb.ColumnTypeText, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(JWTConfigIDPIDCol, handler.ColumnTypeText), + handler.NewColumn(JWTConfigInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(JWTConfigIssuerCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(JWTConfigKeysEndpointCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(JWTConfigHeaderNameCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(JWTConfigEndpointCol, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(JWTConfigInstanceIDCol, JWTConfigIDPIDCol), + handler.NewPrimaryKey(JWTConfigInstanceIDCol, JWTConfigIDPIDCol), IDPJWTSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *idpProjection) reducers() []handler.AggregateReducer { +func (p *idpProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.IDPConfigAddedEventType, Reduce: p.reduceIDPAdded, @@ -165,7 +166,7 @@ func (p *idpProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.IDPConfigAddedEventType, Reduce: p.reduceIDPAdded, @@ -225,7 +226,7 @@ func (p *idpProjection) reduceIDPAdded(event eventstore.Event) (*handler.Stateme return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-fcUdQ", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigAddedEventType, instance.IDPConfigAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &idpEvent, []handler.Column{ handler.NewCol(IDPIDCol, idpEvent.ConfigID), @@ -265,7 +266,7 @@ func (p *idpProjection) reduceIDPChanged(event eventstore.Event) (*handler.State cols = append(cols, handler.NewCol(IDPAutoRegisterCol, *idpEvent.AutoRegister)) } if len(cols) == 0 { - return crdb.NewNoOpStatement(&idpEvent), nil + return handler.NewNoOpStatement(&idpEvent), nil } cols = append(cols, @@ -273,7 +274,7 @@ func (p *idpProjection) reduceIDPChanged(event eventstore.Event) (*handler.State handler.NewCol(IDPSequenceCol, idpEvent.Sequence()), ) - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &idpEvent, cols, []handler.Condition{ @@ -294,7 +295,7 @@ func (p *idpProjection) reduceIDPDeactivated(event eventstore.Event) (*handler.S return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-94O5l", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigDeactivatedEventType, instance.IDPConfigDeactivatedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &idpEvent, []handler.Column{ handler.NewCol(IDPStateCol, domain.IDPConfigStateInactive), @@ -319,7 +320,7 @@ func (p *idpProjection) reduceIDPReactivated(event eventstore.Event) (*handler.S return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-I8QyS", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigReactivatedEventType, instance.IDPConfigReactivatedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &idpEvent, []handler.Column{ handler.NewCol(IDPStateCol, domain.IDPConfigStateActive), @@ -344,7 +345,7 @@ func (p *idpProjection) reduceIDPRemoved(event eventstore.Event) (*handler.State return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-B4zy8", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &idpEvent, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.ConfigID), @@ -364,8 +365,8 @@ func (p *idpProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2FuAA", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPOIDCConfigAddedEventType, instance.IDPOIDCConfigAddedEventType}) } - return crdb.NewMultiStatement(&idpEvent, - crdb.AddUpdateStatement( + return handler.NewMultiStatement(&idpEvent, + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPSequenceCol, idpEvent.Sequence()), @@ -376,20 +377,20 @@ func (p *idpProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), handler.NewCol(OIDCConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(OIDCConfigClientIDCol, idpEvent.ClientID), handler.NewCol(OIDCConfigClientSecretCol, idpEvent.ClientSecret), handler.NewCol(OIDCConfigIssuerCol, idpEvent.Issuer), - handler.NewCol(OIDCConfigScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(OIDCConfigScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(OIDCConfigDisplayNameMappingCol, idpEvent.IDPDisplayNameMapping), handler.NewCol(OIDCConfigUsernameMappingCol, idpEvent.UserNameMapping), handler.NewCol(OIDCConfigAuthorizationEndpointCol, idpEvent.AuthorizationEndpoint), handler.NewCol(OIDCConfigTokenEndpointCol, idpEvent.TokenEndpoint), }, - crdb.WithTableSuffix(IDPOIDCSuffix), + handler.WithTableSuffix(IDPOIDCSuffix), ), ), nil } @@ -423,7 +424,7 @@ func (p *idpProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle cols = append(cols, handler.NewCol(OIDCConfigTokenEndpointCol, *idpEvent.TokenEndpoint)) } if idpEvent.Scopes != nil { - cols = append(cols, handler.NewCol(OIDCConfigScopesCol, database.StringArray(idpEvent.Scopes))) + cols = append(cols, handler.NewCol(OIDCConfigScopesCol, database.TextArray[string](idpEvent.Scopes))) } if idpEvent.IDPDisplayNameMapping != nil { cols = append(cols, handler.NewCol(OIDCConfigDisplayNameMappingCol, *idpEvent.IDPDisplayNameMapping)) @@ -433,11 +434,11 @@ func (p *idpProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle } if len(cols) == 0 { - return crdb.NewNoOpStatement(&idpEvent), nil + return handler.NewNoOpStatement(&idpEvent), nil } - return crdb.NewMultiStatement(&idpEvent, - crdb.AddUpdateStatement( + return handler.NewMultiStatement(&idpEvent, + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPSequenceCol, idpEvent.Sequence()), @@ -447,13 +448,13 @@ func (p *idpProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), handler.NewCond(OIDCConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPOIDCSuffix), + handler.WithTableSuffix(IDPOIDCSuffix), ), ), nil } @@ -469,8 +470,8 @@ func (p *idpProjection) reduceJWTConfigAdded(event eventstore.Event) (*handler.S return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-qvPdb", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPJWTConfigAddedEventType, instance.IDPJWTConfigAddedEventType}) } - return crdb.NewMultiStatement(&idpEvent, - crdb.AddUpdateStatement( + return handler.NewMultiStatement(&idpEvent, + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPSequenceCol, idpEvent.Sequence()), @@ -482,7 +483,7 @@ func (p *idpProjection) reduceJWTConfigAdded(event eventstore.Event) (*handler.S }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(JWTConfigIDPIDCol, idpEvent.IDPConfigID), handler.NewCol(JWTConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -491,7 +492,7 @@ func (p *idpProjection) reduceJWTConfigAdded(event eventstore.Event) (*handler.S handler.NewCol(JWTConfigKeysEndpointCol, idpEvent.KeysEndpoint), handler.NewCol(JWTConfigHeaderNameCol, idpEvent.HeaderName), }, - crdb.WithTableSuffix(IDPJWTSuffix), + handler.WithTableSuffix(IDPJWTSuffix), ), ), nil } @@ -523,11 +524,11 @@ func (p *idpProjection) reduceJWTConfigChanged(event eventstore.Event) (*handler } if len(cols) == 0 { - return crdb.NewNoOpStatement(&idpEvent), nil + return handler.NewNoOpStatement(&idpEvent), nil } - return crdb.NewMultiStatement(&idpEvent, - crdb.AddUpdateStatement( + return handler.NewMultiStatement(&idpEvent, + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPSequenceCol, idpEvent.Sequence()), @@ -537,13 +538,13 @@ func (p *idpProjection) reduceJWTConfigChanged(event eventstore.Event) (*handler handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(JWTConfigIDPIDCol, idpEvent.IDPConfigID), handler.NewCond(JWTConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPJWTSuffix), + handler.WithTableSuffix(IDPJWTSuffix), ), ), nil } @@ -554,7 +555,7 @@ func (p *idpProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.Sta return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-YsbQC", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(IDPInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/idp_login_policy_link.go b/internal/query/projection/idp_login_policy_link.go index 34df691a4..7fcdc5780 100644 --- a/internal/query/projection/idp_login_policy_link.go +++ b/internal/query/projection/idp_login_policy_link.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -27,40 +27,41 @@ const ( IDPLoginPolicyLinkOwnerRemovedCol = "owner_removed" ) -type idpLoginPolicyLinkProjection struct { - crdb.StatementHandler +type idpLoginPolicyLinkProjection struct{} + +func newIDPLoginPolicyLinkProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(idpLoginPolicyLinkProjection)) } -func newIDPLoginPolicyLinkProjection(ctx context.Context, config crdb.StatementHandlerConfig) *idpLoginPolicyLinkProjection { - p := new(idpLoginPolicyLinkProjection) - config.ProjectionName = IDPLoginPolicyLinkTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(IDPLoginPolicyLinkIDPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPLoginPolicyLinkAggregateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPLoginPolicyLinkCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPLoginPolicyLinkChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPLoginPolicyLinkSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(IDPLoginPolicyLinkResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPLoginPolicyLinkInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPLoginPolicyLinkProviderTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPLoginPolicyLinkOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*idpLoginPolicyLinkProjection) Name() string { + return IDPLoginPolicyLinkTable +} + +func (*idpLoginPolicyLinkProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(IDPLoginPolicyLinkIDPIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPLoginPolicyLinkAggregateIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPLoginPolicyLinkCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPLoginPolicyLinkChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPLoginPolicyLinkSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(IDPLoginPolicyLinkResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(IDPLoginPolicyLinkInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPLoginPolicyLinkProviderTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPLoginPolicyLinkOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(IDPLoginPolicyLinkInstanceIDCol, IDPLoginPolicyLinkAggregateIDCol, IDPLoginPolicyLinkIDPIDCol), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{IDPLoginPolicyLinkResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{IDPLoginPolicyLinkOwnerRemovedCol})), + handler.NewPrimaryKey(IDPLoginPolicyLinkInstanceIDCol, IDPLoginPolicyLinkAggregateIDCol, IDPLoginPolicyLinkIDPIDCol), + handler.WithIndex(handler.NewIndex("resource_owner", []string{IDPLoginPolicyLinkResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{IDPLoginPolicyLinkOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *idpLoginPolicyLinkProjection) reducers() []handler.AggregateReducer { +func (p *idpLoginPolicyLinkProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.LoginPolicyIDPProviderAddedEventType, Reduce: p.reduceAdded, @@ -93,7 +94,7 @@ func (p *idpLoginPolicyLinkProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.LoginPolicyIDPProviderAddedEventType, Reduce: p.reduceAdded, @@ -140,7 +141,7 @@ func (p *idpLoginPolicyLinkProjection) reduceAdded(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Nlp55", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyIDPProviderAddedEventType, instance.LoginPolicyIDPProviderAddedEventType}) } - return crdb.NewCreateStatement(&idp, + return handler.NewCreateStatement(&idp, []handler.Column{ handler.NewCol(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), handler.NewCol(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), @@ -166,7 +167,7 @@ func (p *idpLoginPolicyLinkProjection) reduceRemoved(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-tUMYY", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyIDPProviderRemovedEventType, instance.LoginPolicyIDPProviderRemovedEventType}) } - return crdb.NewDeleteStatement(&idp, + return handler.NewDeleteStatement(&idp, []handler.Condition{ handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), handler.NewCond(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), @@ -187,7 +188,7 @@ func (p *idpLoginPolicyLinkProjection) reduceCascadeRemoved(event eventstore.Eve return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-iCKSj", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyIDPProviderCascadeRemovedEventType, instance.LoginPolicyIDPProviderCascadeRemovedEventType}) } - return crdb.NewDeleteStatement(&idp, + return handler.NewDeleteStatement(&idp, []handler.Condition{ handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), handler.NewCond(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), @@ -215,7 +216,7 @@ func (p *idpLoginPolicyLinkProjection) reduceIDPConfigRemoved(event eventstore.E return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-u6tze", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) } - return crdb.NewDeleteStatement(event, conditions), nil + return handler.NewDeleteStatement(event, conditions), nil } func (p *idpLoginPolicyLinkProjection) reduceIDPRemoved(event eventstore.Event) (*handler.Statement, error) { @@ -237,7 +238,7 @@ func (p *idpLoginPolicyLinkProjection) reduceIDPRemoved(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SFED3", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPRemovedEventType, instance.IDPRemovedEventType}) } - return crdb.NewDeleteStatement(event, conditions), nil + return handler.NewDeleteStatement(event, conditions), nil } func (p *idpLoginPolicyLinkProjection) reducePolicyRemoved(event eventstore.Event) (*handler.Statement, error) { @@ -245,7 +246,7 @@ func (p *idpLoginPolicyLinkProjection) reducePolicyRemoved(event eventstore.Even if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SF3dg", "reduce.wrong.event.type %s", org.LoginPolicyRemovedEventType) } - return crdb.NewDeleteStatement(e, + return handler.NewDeleteStatement(e, []handler.Condition{ handler.NewCond(IDPLoginPolicyLinkAggregateIDCol, e.Aggregate().ID), handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), @@ -259,7 +260,7 @@ func (p *idpLoginPolicyLinkProjection) reduceOwnerRemoved(event eventstore.Event return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-YbhOv", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/idp_login_policy_link_test.go b/internal/query/projection/idp_login_policy_link_test.go index 569f3746b..4f58ec1fa 100644 --- a/internal/query/projection/idp_login_policy_link_test.go +++ b/internal/query/projection/idp_login_policy_link_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,20 +24,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "iam reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyIDPProviderAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyIDPProviderAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), instance.IdentityProviderAddedEventMapper), + ), instance.IdentityProviderAddedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceAdded, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -61,20 +60,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "iam reduceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyIDPProviderRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyIDPProviderRemovedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), instance.IdentityProviderRemovedEventMapper), + ), instance.IdentityProviderRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -92,20 +91,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "iam reduceCascadeRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyIDPProviderCascadeRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyIDPProviderCascadeRemovedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), instance.IdentityProviderCascadeRemovedEventMapper), + ), instance.IdentityProviderCascadeRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -123,20 +122,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyIDPProviderAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyIDPProviderAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), org.IdentityProviderAddedEventMapper), + ), org.IdentityProviderAddedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceAdded, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -159,20 +158,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "org reduceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyIDPProviderRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyIDPProviderRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), org.IdentityProviderRemovedEventMapper), + ), org.IdentityProviderRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -190,17 +189,17 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(IDPUserLinkInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -216,20 +215,20 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "org reduceCascadeRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyIDPProviderCascadeRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyIDPProviderCascadeRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "idpProviderType": 1 }`), - ), org.IdentityProviderCascadeRemovedEventMapper), + ), org.IdentityProviderCascadeRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -247,17 +246,17 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "reducePolicyRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.LoginPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.LoginPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.LoginPolicyRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reducePolicyRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -274,19 +273,19 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "org IDPConfigRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), org.IDPConfigRemovedEventMapper), + ), org.IDPConfigRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceIDPConfigRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -304,19 +303,19 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "iam IDPConfigRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigRemovedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), instance.IDPConfigRemovedEventMapper), + ), instance.IDPConfigRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceIDPConfigRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -333,19 +332,19 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "org IDPRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPRemovedEventType, + org.AggregateType, + []byte(`{ "id": "id" }`), - ), org.IDPRemovedEventMapper), + ), org.IDPRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceIDPRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -363,19 +362,19 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { { name: "iam IDPRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPRemovedEventType, + instance.AggregateType, + []byte(`{ "id": "id" }`), - ), instance.IDPRemovedEventMapper), + ), instance.IDPRemovedEventMapper), }, reduce: (&idpLoginPolicyLinkProjection{}).reduceIDPRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -393,16 +392,16 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&idpLoginPolicyLinkProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index 715b853c5..181fecefb 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -8,8 +8,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/instance" @@ -169,214 +169,215 @@ const ( SAMLWithSignedRequestCol = "with_signed_request" ) -type idpTemplateProjection struct { - crdb.StatementHandler +type idpTemplateProjection struct{} + +func newIDPTemplateProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(idpTemplateProjection)) } -func newIDPTemplateProjection(ctx context.Context, config crdb.StatementHandlerConfig) *idpTemplateProjection { - p := new(idpTemplateProjection) - config.ProjectionName = IDPTemplateTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(IDPTemplateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPTemplateCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPTemplateChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPTemplateSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(IDPTemplateResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPTemplateInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPTemplateStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPTemplateNameCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(IDPTemplateOwnerTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPTemplateTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(IDPTemplateOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(IDPTemplateIsCreationAllowedCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(IDPTemplateIsLinkingAllowedCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(IDPTemplateIsAutoCreationCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(IDPTemplateIsAutoUpdateCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*idpTemplateProjection) Name() string { + return IDPTemplateTable +} + +func (*idpTemplateProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(IDPTemplateIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPTemplateCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPTemplateChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPTemplateSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(IDPTemplateResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(IDPTemplateInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPTemplateStateCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPTemplateNameCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(IDPTemplateOwnerTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPTemplateTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(IDPTemplateOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(IDPTemplateIsCreationAllowedCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(IDPTemplateIsLinkingAllowedCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(IDPTemplateIsAutoCreationCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(IDPTemplateIsAutoUpdateCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(IDPTemplateInstanceIDCol, IDPTemplateIDCol), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{IDPTemplateResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{IDPTemplateOwnerRemovedCol})), + handler.NewPrimaryKey(IDPTemplateInstanceIDCol, IDPTemplateIDCol), + handler.WithIndex(handler.NewIndex("resource_owner", []string{IDPTemplateResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{IDPTemplateOwnerRemovedCol})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(OAuthIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(OAuthAuthorizationEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthTokenEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthUserEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(OAuthScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(OAuthIDAttributeCol, crdb.ColumnTypeText), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(OAuthIDCol, handler.ColumnTypeText), + handler.NewColumn(OAuthInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(OAuthClientIDCol, handler.ColumnTypeText), + handler.NewColumn(OAuthClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(OAuthAuthorizationEndpointCol, handler.ColumnTypeText), + handler.NewColumn(OAuthTokenEndpointCol, handler.ColumnTypeText), + handler.NewColumn(OAuthUserEndpointCol, handler.ColumnTypeText), + handler.NewColumn(OAuthScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(OAuthIDAttributeCol, handler.ColumnTypeText), }, - crdb.NewPrimaryKey(OAuthInstanceIDCol, OAuthIDCol), + handler.NewPrimaryKey(OAuthInstanceIDCol, OAuthIDCol), IDPTemplateOAuthSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(OIDCIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCIssuerCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OIDCClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(OIDCScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(OIDCIDTokenMappingCol, crdb.ColumnTypeBool, crdb.Default(false)), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(OIDCIDCol, handler.ColumnTypeText), + handler.NewColumn(OIDCInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(OIDCIssuerCol, handler.ColumnTypeText), + handler.NewColumn(OIDCClientIDCol, handler.ColumnTypeText), + handler.NewColumn(OIDCClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(OIDCScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(OIDCIDTokenMappingCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(OIDCInstanceIDCol, OIDCIDCol), + handler.NewPrimaryKey(OIDCInstanceIDCol, OIDCIDCol), IDPTemplateOIDCSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(JWTIDCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTIssuerCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTKeysEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(JWTHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(JWTIDCol, handler.ColumnTypeText), + handler.NewColumn(JWTInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(JWTIssuerCol, handler.ColumnTypeText), + handler.NewColumn(JWTEndpointCol, handler.ColumnTypeText), + handler.NewColumn(JWTKeysEndpointCol, handler.ColumnTypeText), + handler.NewColumn(JWTHeaderNameCol, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(JWTInstanceIDCol, JWTIDCol), + handler.NewPrimaryKey(JWTInstanceIDCol, JWTIDCol), IDPTemplateJWTSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AzureADIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AzureADInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AzureADClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AzureADClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(AzureADScopesCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(AzureADTenantCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(AzureADIsEmailVerified, crdb.ColumnTypeBool, crdb.Default(false)), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(AzureADIDCol, handler.ColumnTypeText), + handler.NewColumn(AzureADInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(AzureADClientIDCol, handler.ColumnTypeText), + handler.NewColumn(AzureADClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(AzureADScopesCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(AzureADTenantCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(AzureADIsEmailVerified, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(AzureADInstanceIDCol, AzureADIDCol), + handler.NewPrimaryKey(AzureADInstanceIDCol, AzureADIDCol), IDPTemplateAzureADSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(GitHubIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(GitHubScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(GitHubIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubClientIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(GitHubScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(GitHubInstanceIDCol, GitHubIDCol), + handler.NewPrimaryKey(GitHubInstanceIDCol, GitHubIDCol), IDPTemplateGitHubSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(GitHubEnterpriseIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(GitHubEnterpriseAuthorizationEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseTokenEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseUserEndpointCol, crdb.ColumnTypeText), - crdb.NewColumn(GitHubEnterpriseScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(GitHubEnterpriseIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseClientIDCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(GitHubEnterpriseAuthorizationEndpointCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseTokenEndpointCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseUserEndpointCol, handler.ColumnTypeText), + handler.NewColumn(GitHubEnterpriseScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(GitHubEnterpriseInstanceIDCol, GitHubEnterpriseIDCol), + handler.NewPrimaryKey(GitHubEnterpriseInstanceIDCol, GitHubEnterpriseIDCol), IDPTemplateGitHubEnterpriseSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(GitLabIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(GitLabScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(GitLabIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabClientIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(GitLabScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(GitLabInstanceIDCol, GitLabIDCol), + handler.NewPrimaryKey(GitLabInstanceIDCol, GitLabIDCol), IDPTemplateGitLabSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(GitLabSelfHostedIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabSelfHostedInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabSelfHostedIssuerCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabSelfHostedClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GitLabSelfHostedClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(GitLabSelfHostedScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(GitLabSelfHostedIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabSelfHostedInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabSelfHostedIssuerCol, handler.ColumnTypeText), + handler.NewColumn(GitLabSelfHostedClientIDCol, handler.ColumnTypeText), + handler.NewColumn(GitLabSelfHostedClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(GitLabSelfHostedScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(GitLabSelfHostedInstanceIDCol, GitLabSelfHostedIDCol), + handler.NewPrimaryKey(GitLabSelfHostedInstanceIDCol, GitLabSelfHostedIDCol), IDPTemplateGitLabSelfHostedSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(GoogleIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GoogleInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GoogleClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(GoogleClientSecretCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(GoogleScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(GoogleIDCol, handler.ColumnTypeText), + handler.NewColumn(GoogleInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(GoogleClientIDCol, handler.ColumnTypeText), + handler.NewColumn(GoogleClientSecretCol, handler.ColumnTypeJSONB), + handler.NewColumn(GoogleScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(GoogleInstanceIDCol, GoogleIDCol), + handler.NewPrimaryKey(GoogleInstanceIDCol, GoogleIDCol), IDPTemplateGoogleSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(LDAPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LDAPInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LDAPServersCol, crdb.ColumnTypeTextArray), - crdb.NewColumn(LDAPStartTLSCol, crdb.ColumnTypeBool), - crdb.NewColumn(LDAPBaseDNCol, crdb.ColumnTypeText), - crdb.NewColumn(LDAPBindDNCol, crdb.ColumnTypeText), - crdb.NewColumn(LDAPBindPasswordCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(LDAPUserBaseCol, crdb.ColumnTypeText), - crdb.NewColumn(LDAPUserObjectClassesCol, crdb.ColumnTypeTextArray), - crdb.NewColumn(LDAPUserFiltersCol, crdb.ColumnTypeTextArray), - crdb.NewColumn(LDAPTimeoutCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LDAPIDAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPFirstNameAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPLastNameAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPDisplayNameAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPNickNameAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPPreferredUsernameAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPEmailAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPEmailVerifiedAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPPhoneAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPPhoneVerifiedAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPPreferredLanguageAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPAvatarURLAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LDAPProfileAttributeCol, crdb.ColumnTypeText, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(LDAPIDCol, handler.ColumnTypeText), + handler.NewColumn(LDAPInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LDAPServersCol, handler.ColumnTypeTextArray), + handler.NewColumn(LDAPStartTLSCol, handler.ColumnTypeBool), + handler.NewColumn(LDAPBaseDNCol, handler.ColumnTypeText), + handler.NewColumn(LDAPBindDNCol, handler.ColumnTypeText), + handler.NewColumn(LDAPBindPasswordCol, handler.ColumnTypeJSONB), + handler.NewColumn(LDAPUserBaseCol, handler.ColumnTypeText), + handler.NewColumn(LDAPUserObjectClassesCol, handler.ColumnTypeTextArray), + handler.NewColumn(LDAPUserFiltersCol, handler.ColumnTypeTextArray), + handler.NewColumn(LDAPTimeoutCol, handler.ColumnTypeInt64), + handler.NewColumn(LDAPIDAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPFirstNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPLastNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPDisplayNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPNickNameAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPPreferredUsernameAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPEmailAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPEmailVerifiedAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPPhoneAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPPhoneVerifiedAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPPreferredLanguageAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPAvatarURLAttributeCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LDAPProfileAttributeCol, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(LDAPInstanceIDCol, LDAPIDCol), + handler.NewPrimaryKey(LDAPInstanceIDCol, LDAPIDCol), IDPTemplateLDAPSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppleIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AppleInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AppleClientIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AppleTeamIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AppleKeyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ApplePrivateKeyCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(AppleScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(AppleIDCol, handler.ColumnTypeText), + handler.NewColumn(AppleInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(AppleClientIDCol, handler.ColumnTypeText), + handler.NewColumn(AppleTeamIDCol, handler.ColumnTypeText), + handler.NewColumn(AppleKeyIDCol, handler.ColumnTypeText), + handler.NewColumn(ApplePrivateKeyCol, handler.ColumnTypeJSONB), + handler.NewColumn(AppleScopesCol, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(AppleInstanceIDCol, AppleIDCol), + handler.NewPrimaryKey(AppleInstanceIDCol, AppleIDCol), IDPTemplateAppleSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(SAMLIDCol, crdb.ColumnTypeText), - crdb.NewColumn(SAMLInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(SAMLMetadataCol, crdb.ColumnTypeBytes), - crdb.NewColumn(SAMLKeyCol, crdb.ColumnTypeJSONB), - crdb.NewColumn(SAMLCertificateCol, crdb.ColumnTypeBytes), - crdb.NewColumn(SAMLBindingCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(SAMLWithSignedRequestCol, crdb.ColumnTypeBool, crdb.Nullable()), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(SAMLIDCol, handler.ColumnTypeText), + handler.NewColumn(SAMLInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(SAMLMetadataCol, handler.ColumnTypeBytes), + handler.NewColumn(SAMLKeyCol, handler.ColumnTypeJSONB), + handler.NewColumn(SAMLCertificateCol, handler.ColumnTypeBytes), + handler.NewColumn(SAMLBindingCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SAMLWithSignedRequestCol, handler.ColumnTypeBool, handler.Nullable()), }, - crdb.NewPrimaryKey(SAMLInstanceIDCol, SAMLIDCol), + handler.NewPrimaryKey(SAMLInstanceIDCol, SAMLIDCol), IDPTemplateSAMLSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { +func (p *idpTemplateProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.OAuthIDPAddedEventType, Reduce: p.reduceOAuthIDPAdded, @@ -521,7 +522,7 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OAuthIDPAddedEventType, Reduce: p.reduceOAuthIDPAdded, @@ -681,9 +682,9 @@ func (p *idpTemplateProjection) reduceOAuthIDPAdded(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ap9ihb", "reduce.wrong.event.type %v", []eventstore.EventType{org.OAuthIDPAddedEventType, instance.OAuthIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -701,7 +702,7 @@ func (p *idpTemplateProjection) reduceOAuthIDPAdded(event eventstore.Event) (*ha handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(OAuthIDCol, idpEvent.ID), handler.NewCol(OAuthInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -710,10 +711,10 @@ func (p *idpTemplateProjection) reduceOAuthIDPAdded(event eventstore.Event) (*ha handler.NewCol(OAuthAuthorizationEndpointCol, idpEvent.AuthorizationEndpoint), handler.NewCol(OAuthTokenEndpointCol, idpEvent.TokenEndpoint), handler.NewCol(OAuthUserEndpointCol, idpEvent.UserEndpoint), - handler.NewCol(OAuthScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(OAuthScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(OAuthIDAttributeCol, idpEvent.IDAttribute), }, - crdb.WithTableSuffix(IDPTemplateOAuthSuffix), + handler.WithTableSuffix(IDPTemplateOAuthSuffix), ), ), nil } @@ -729,9 +730,9 @@ func (p *idpTemplateProjection) reduceOAuthIDPChanged(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OAuthIDPChangedEventType, instance.OAuthIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -742,18 +743,18 @@ func (p *idpTemplateProjection) reduceOAuthIDPChanged(event eventstore.Event) (* oauthCols := reduceOAuthIDPChangedColumns(idpEvent) if len(oauthCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( oauthCols, []handler.Condition{ handler.NewCond(OAuthIDCol, idpEvent.ID), handler.NewCond(OAuthInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateOAuthSuffix), + handler.WithTableSuffix(IDPTemplateOAuthSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -773,9 +774,9 @@ func (p *idpTemplateProjection) reduceOIDCIDPAdded(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPAddedEventType, instance.OIDCIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -793,17 +794,17 @@ func (p *idpTemplateProjection) reduceOIDCIDPAdded(event eventstore.Event) (*han handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(OIDCIDCol, idpEvent.ID), handler.NewCol(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(OIDCIssuerCol, idpEvent.Issuer), handler.NewCol(OIDCClientIDCol, idpEvent.ClientID), handler.NewCol(OIDCClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(OIDCScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(OIDCIDTokenMappingCol, idpEvent.IsIDTokenMapping), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), ), nil } @@ -819,9 +820,9 @@ func (p *idpTemplateProjection) reduceOIDCIDPChanged(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPChangedEventType, instance.OIDCIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -832,18 +833,18 @@ func (p *idpTemplateProjection) reduceOIDCIDPChanged(event eventstore.Event) (*h oidcCols := reduceOIDCIDPChangedColumns(idpEvent) if len(oidcCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( oidcCols, []handler.Condition{ handler.NewCond(OIDCIDCol, idpEvent.ID), handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -860,9 +861,9 @@ func (p *idpTemplateProjection) reduceOIDCIDPMigratedAzureAD(event eventstore.Ev return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedAzureADEventType, instance.OIDCIDPMigratedAzureADEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -878,24 +879,24 @@ func (p *idpTemplateProjection) reduceOIDCIDPMigratedAzureAD(event eventstore.Ev handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(OIDCIDCol, idpEvent.ID), handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AzureADIDCol, idpEvent.ID), handler.NewCol(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(AzureADClientIDCol, idpEvent.ClientID), handler.NewCol(AzureADClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(AzureADScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(AzureADTenantCol, idpEvent.Tenant), handler.NewCol(AzureADIsEmailVerified, idpEvent.IsEmailVerified), }, - crdb.WithTableSuffix(IDPTemplateAzureADSuffix), + handler.WithTableSuffix(IDPTemplateAzureADSuffix), ), ), nil } @@ -911,9 +912,9 @@ func (p *idpTemplateProjection) reduceOIDCIDPMigratedGoogle(event eventstore.Eve return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPMigratedGoogleEventType, instance.OIDCIDPMigratedGoogleEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -929,22 +930,22 @@ func (p *idpTemplateProjection) reduceOIDCIDPMigratedGoogle(event eventstore.Eve handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(OIDCIDCol, idpEvent.ID), handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GoogleIDCol, idpEvent.ID), handler.NewCol(GoogleInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(GoogleClientIDCol, idpEvent.ClientID), handler.NewCol(GoogleClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(GoogleScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GoogleScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGoogleSuffix), + handler.WithTableSuffix(IDPTemplateGoogleSuffix), ), ), nil } @@ -963,9 +964,9 @@ func (p *idpTemplateProjection) reduceJWTIDPAdded(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-xopi2s", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPAddedEventType, instance.JWTIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -983,7 +984,7 @@ func (p *idpTemplateProjection) reduceJWTIDPAdded(event eventstore.Event) (*hand handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(JWTIDCol, idpEvent.ID), handler.NewCol(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -992,7 +993,7 @@ func (p *idpTemplateProjection) reduceJWTIDPAdded(event eventstore.Event) (*hand handler.NewCol(JWTKeysEndpointCol, idpEvent.KeysEndpoint), handler.NewCol(JWTHeaderNameCol, idpEvent.HeaderName), }, - crdb.WithTableSuffix(IDPTemplateJWTSuffix), + handler.WithTableSuffix(IDPTemplateJWTSuffix), ), ), nil } @@ -1008,9 +1009,9 @@ func (p *idpTemplateProjection) reduceJWTIDPChanged(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPChangedEventType, instance.JWTIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1021,18 +1022,18 @@ func (p *idpTemplateProjection) reduceJWTIDPChanged(event eventstore.Event) (*ha jwtCols := reduceJWTIDPChangedColumns(idpEvent) if len(jwtCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( jwtCols, []handler.Condition{ handler.NewCond(JWTIDCol, idpEvent.ID), handler.NewCond(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateJWTSuffix), + handler.WithTableSuffix(IDPTemplateJWTSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1052,7 +1053,7 @@ func (p *idpTemplateProjection) reduceOldConfigAdded(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ADfeg", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigAddedEventType, instance.IDPConfigAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( event, []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ConfigID), @@ -1096,7 +1097,7 @@ func (p *idpTemplateProjection) reduceOldConfigChanged(event eventstore.Event) ( handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), ) - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, cols, []handler.Condition{ @@ -1117,9 +1118,9 @@ func (p *idpTemplateProjection) reduceOldOIDCConfigAdded(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASFdq2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPOIDCConfigAddedEventType, instance.IDPOIDCConfigAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -1130,17 +1131,17 @@ func (p *idpTemplateProjection) reduceOldOIDCConfigAdded(event eventstore.Event) handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(OIDCIDCol, idpEvent.IDPConfigID), handler.NewCol(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(OIDCIssuerCol, idpEvent.Issuer), handler.NewCol(OIDCClientIDCol, idpEvent.ClientID), handler.NewCol(OIDCClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(OIDCScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(OIDCIDTokenMappingCol, true), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), ), nil } @@ -1156,9 +1157,9 @@ func (p *idpTemplateProjection) reduceOldOIDCConfigChanged(event eventstore.Even return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPChangedEventType, instance.OIDCIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -1180,22 +1181,22 @@ func (p *idpTemplateProjection) reduceOldOIDCConfigChanged(event eventstore.Even oidcCols = append(oidcCols, handler.NewCol(OIDCIssuerCol, *idpEvent.Issuer)) } if idpEvent.Scopes != nil { - oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes))) + oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.TextArray[string](idpEvent.Scopes))) } if len(oidcCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( oidcCols, []handler.Condition{ handler.NewCond(OIDCIDCol, idpEvent.IDPConfigID), handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + handler.WithTableSuffix(IDPTemplateOIDCSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1212,9 +1213,9 @@ func (p *idpTemplateProjection) reduceOldJWTConfigAdded(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASFdq2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPJWTConfigAddedEventType, instance.IDPJWTConfigAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -1225,7 +1226,7 @@ func (p *idpTemplateProjection) reduceOldJWTConfigAdded(event eventstore.Event) handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(JWTIDCol, idpEvent.IDPConfigID), handler.NewCol(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -1234,7 +1235,7 @@ func (p *idpTemplateProjection) reduceOldJWTConfigAdded(event eventstore.Event) handler.NewCol(JWTKeysEndpointCol, idpEvent.KeysEndpoint), handler.NewCol(JWTHeaderNameCol, idpEvent.HeaderName), }, - crdb.WithTableSuffix(IDPTemplateJWTSuffix), + handler.WithTableSuffix(IDPTemplateJWTSuffix), ), ), nil } @@ -1250,9 +1251,9 @@ func (p *idpTemplateProjection) reduceOldJWTConfigChanged(event eventstore.Event return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPChangedEventType, instance.JWTIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), @@ -1278,18 +1279,18 @@ func (p *idpTemplateProjection) reduceOldJWTConfigChanged(event eventstore.Event } if len(jwtCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( jwtCols, []handler.Condition{ handler.NewCond(JWTIDCol, idpEvent.IDPConfigID), handler.NewCond(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateJWTSuffix), + handler.WithTableSuffix(IDPTemplateJWTSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1309,9 +1310,9 @@ func (p *idpTemplateProjection) reduceAzureADIDPAdded(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPAddedEventType, instance.AzureADIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1329,17 +1330,17 @@ func (p *idpTemplateProjection) reduceAzureADIDPAdded(event eventstore.Event) (* handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AzureADIDCol, idpEvent.ID), handler.NewCol(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(AzureADClientIDCol, idpEvent.ClientID), handler.NewCol(AzureADClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(AzureADScopesCol, database.TextArray[string](idpEvent.Scopes)), handler.NewCol(AzureADTenantCol, idpEvent.Tenant), handler.NewCol(AzureADIsEmailVerified, idpEvent.IsEmailVerified), }, - crdb.WithTableSuffix(IDPTemplateAzureADSuffix), + handler.WithTableSuffix(IDPTemplateAzureADSuffix), ), ), nil } @@ -1355,9 +1356,9 @@ func (p *idpTemplateProjection) reduceAzureADIDPChanged(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPChangedEventType, instance.AzureADIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1368,18 +1369,18 @@ func (p *idpTemplateProjection) reduceAzureADIDPChanged(event eventstore.Event) githubCols := reduceAzureADIDPChangedColumns(idpEvent) if len(githubCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( githubCols, []handler.Condition{ handler.NewCond(AzureADIDCol, idpEvent.ID), handler.NewCond(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateAzureADSuffix), + handler.WithTableSuffix(IDPTemplateAzureADSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1399,9 +1400,9 @@ func (p *idpTemplateProjection) reduceGitHubIDPAdded(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPAddedEventType, instance.GitHubIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1419,15 +1420,15 @@ func (p *idpTemplateProjection) reduceGitHubIDPAdded(event eventstore.Event) (*h handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GitHubIDCol, idpEvent.ID), handler.NewCol(GitHubInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(GitHubClientIDCol, idpEvent.ClientID), handler.NewCol(GitHubClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(GitHubScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GitHubScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGitHubSuffix), + handler.WithTableSuffix(IDPTemplateGitHubSuffix), ), ), nil } @@ -1446,9 +1447,9 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPAdded(event eventstore. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sf3g2a", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPAddedEventType, instance.GitHubEnterpriseIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1466,7 +1467,7 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPAdded(event eventstore. handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GitHubEnterpriseIDCol, idpEvent.ID), handler.NewCol(GitHubEnterpriseInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -1475,9 +1476,9 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPAdded(event eventstore. handler.NewCol(GitHubEnterpriseAuthorizationEndpointCol, idpEvent.AuthorizationEndpoint), handler.NewCol(GitHubEnterpriseTokenEndpointCol, idpEvent.TokenEndpoint), handler.NewCol(GitHubEnterpriseUserEndpointCol, idpEvent.UserEndpoint), - handler.NewCol(GitHubEnterpriseScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GitHubEnterpriseScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), + handler.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), ), ), nil } @@ -1493,9 +1494,9 @@ func (p *idpTemplateProjection) reduceGitHubIDPChanged(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubIDPChangedEventType, instance.GitHubIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1506,18 +1507,18 @@ func (p *idpTemplateProjection) reduceGitHubIDPChanged(event eventstore.Event) ( githubCols := reduceGitHubIDPChangedColumns(idpEvent) if len(githubCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( githubCols, []handler.Condition{ handler.NewCond(GitHubIDCol, idpEvent.ID), handler.NewCond(GitHubInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateGitHubSuffix), + handler.WithTableSuffix(IDPTemplateGitHubSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1534,9 +1535,9 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPChanged(event eventstor return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDg3g", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitHubEnterpriseIDPChangedEventType, instance.GitHubEnterpriseIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1547,18 +1548,18 @@ func (p *idpTemplateProjection) reduceGitHubEnterpriseIDPChanged(event eventstor githubCols := reduceGitHubEnterpriseIDPChangedColumns(idpEvent) if len(githubCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( githubCols, []handler.Condition{ handler.NewCond(GitHubEnterpriseIDCol, idpEvent.ID), handler.NewCond(GitHubEnterpriseInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), + handler.WithTableSuffix(IDPTemplateGitHubEnterpriseSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1578,9 +1579,9 @@ func (p *idpTemplateProjection) reduceGitLabIDPAdded(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabIDPAddedEventType, instance.GitLabIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1598,15 +1599,15 @@ func (p *idpTemplateProjection) reduceGitLabIDPAdded(event eventstore.Event) (*h handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GitLabIDCol, idpEvent.ID), handler.NewCol(GitLabInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(GitLabClientIDCol, idpEvent.ClientID), handler.NewCol(GitLabClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(GitLabScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GitLabScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGitLabSuffix), + handler.WithTableSuffix(IDPTemplateGitLabSuffix), ), ), nil } @@ -1622,9 +1623,9 @@ func (p *idpTemplateProjection) reduceGitLabIDPChanged(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabIDPChangedEventType, instance.GitLabIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1635,18 +1636,18 @@ func (p *idpTemplateProjection) reduceGitLabIDPChanged(event eventstore.Event) ( gitlabCols := reduceGitLabIDPChangedColumns(idpEvent) if len(gitlabCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( gitlabCols, []handler.Condition{ handler.NewCond(GitLabIDCol, idpEvent.ID), handler.NewCond(GitLabInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateGitLabSuffix), + handler.WithTableSuffix(IDPTemplateGitLabSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1666,9 +1667,9 @@ func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPAdded(event eventstore. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAF3gw", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabSelfHostedIDPAddedEventType, instance.GitLabSelfHostedIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1686,16 +1687,16 @@ func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPAdded(event eventstore. handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GitLabSelfHostedIDCol, idpEvent.ID), handler.NewCol(GitLabSelfHostedInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(GitLabSelfHostedIssuerCol, idpEvent.Issuer), handler.NewCol(GitLabSelfHostedClientIDCol, idpEvent.ClientID), handler.NewCol(GitLabSelfHostedClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(GitLabSelfHostedScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GitLabSelfHostedScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), + handler.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), ), ), nil } @@ -1711,9 +1712,9 @@ func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPChanged(event eventstor return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAf3g2", "reduce.wrong.event.type %v", []eventstore.EventType{org.GitLabSelfHostedIDPChangedEventType, instance.GitLabSelfHostedIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1724,18 +1725,18 @@ func (p *idpTemplateProjection) reduceGitLabSelfHostedIDPChanged(event eventstor gitlabCols := reduceGitLabSelfHostedIDPChangedColumns(idpEvent) if len(gitlabCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( gitlabCols, []handler.Condition{ handler.NewCond(GitLabSelfHostedIDCol, idpEvent.ID), handler.NewCond(GitLabSelfHostedInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), + handler.WithTableSuffix(IDPTemplateGitLabSelfHostedSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1755,9 +1756,9 @@ func (p *idpTemplateProjection) reduceGoogleIDPAdded(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ap9ihb", "reduce.wrong.event.type %v", []eventstore.EventType{org.GoogleIDPAddedEventType, instance.GoogleIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1775,15 +1776,15 @@ func (p *idpTemplateProjection) reduceGoogleIDPAdded(event eventstore.Event) (*h handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(GoogleIDCol, idpEvent.ID), handler.NewCol(GoogleInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(GoogleClientIDCol, idpEvent.ClientID), handler.NewCol(GoogleClientSecretCol, idpEvent.ClientSecret), - handler.NewCol(GoogleScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(GoogleScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateGoogleSuffix), + handler.WithTableSuffix(IDPTemplateGoogleSuffix), ), ), nil } @@ -1799,9 +1800,9 @@ func (p *idpTemplateProjection) reduceGoogleIDPChanged(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.GoogleIDPChangedEventType, instance.GoogleIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1812,18 +1813,18 @@ func (p *idpTemplateProjection) reduceGoogleIDPChanged(event eventstore.Event) ( googleCols := reduceGoogleIDPChangedColumns(idpEvent) if len(googleCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( googleCols, []handler.Condition{ handler.NewCond(GoogleIDCol, idpEvent.ID), handler.NewCond(GoogleInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateGoogleSuffix), + handler.WithTableSuffix(IDPTemplateGoogleSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1843,9 +1844,9 @@ func (p *idpTemplateProjection) reduceLDAPIDPAdded(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPAddedEventType, instance.LDAPIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1863,18 +1864,18 @@ func (p *idpTemplateProjection) reduceLDAPIDPAdded(event eventstore.Event) (*han handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(LDAPIDCol, idpEvent.ID), handler.NewCol(LDAPInstanceIDCol, idpEvent.Aggregate().InstanceID), - handler.NewCol(LDAPServersCol, database.StringArray(idpEvent.Servers)), + handler.NewCol(LDAPServersCol, database.TextArray[string](idpEvent.Servers)), handler.NewCol(LDAPStartTLSCol, idpEvent.StartTLS), handler.NewCol(LDAPBaseDNCol, idpEvent.BaseDN), handler.NewCol(LDAPBindDNCol, idpEvent.BindDN), handler.NewCol(LDAPBindPasswordCol, idpEvent.BindPassword), handler.NewCol(LDAPUserBaseCol, idpEvent.UserBase), - handler.NewCol(LDAPUserObjectClassesCol, database.StringArray(idpEvent.UserObjectClasses)), - handler.NewCol(LDAPUserFiltersCol, database.StringArray(idpEvent.UserFilters)), + handler.NewCol(LDAPUserObjectClassesCol, database.TextArray[string](idpEvent.UserObjectClasses)), + handler.NewCol(LDAPUserFiltersCol, database.TextArray[string](idpEvent.UserFilters)), handler.NewCol(LDAPTimeoutCol, idpEvent.Timeout), handler.NewCol(LDAPIDAttributeCol, idpEvent.IDAttribute), handler.NewCol(LDAPFirstNameAttributeCol, idpEvent.FirstNameAttribute), @@ -1890,7 +1891,7 @@ func (p *idpTemplateProjection) reduceLDAPIDPAdded(event eventstore.Event) (*han handler.NewCol(LDAPAvatarURLAttributeCol, idpEvent.AvatarURLAttribute), handler.NewCol(LDAPProfileAttributeCol, idpEvent.ProfileAttribute), }, - crdb.WithTableSuffix(IDPTemplateLDAPSuffix), + handler.WithTableSuffix(IDPTemplateLDAPSuffix), ), ), nil } @@ -1906,9 +1907,9 @@ func (p *idpTemplateProjection) reduceLDAPIDPChanged(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.LDAPIDPChangedEventType, instance.LDAPIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -1920,18 +1921,18 @@ func (p *idpTemplateProjection) reduceLDAPIDPChanged(event eventstore.Event) (*h ldapCols := reduceLDAPIDPChangedColumns(idpEvent) if len(ldapCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( ldapCols, []handler.Condition{ handler.NewCond(LDAPIDCol, idpEvent.ID), handler.NewCond(LDAPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateLDAPSuffix), + handler.WithTableSuffix(IDPTemplateLDAPSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -1951,9 +1952,9 @@ func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPAddedEventType, instance.SAMLIDPAddedEventType}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -1971,7 +1972,7 @@ func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*han handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(SAMLIDCol, idpEvent.ID), handler.NewCol(SAMLInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -1981,7 +1982,7 @@ func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*han handler.NewCol(SAMLBindingCol, idpEvent.Binding), handler.NewCol(SAMLWithSignedRequestCol, idpEvent.WithSignedRequest), }, - crdb.WithTableSuffix(IDPTemplateSAMLSuffix), + handler.WithTableSuffix(IDPTemplateSAMLSuffix), ), ), nil } @@ -1997,9 +1998,9 @@ func (p *idpTemplateProjection) reduceSAMLIDPChanged(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-o7c0fii4ad", "reduce.wrong.event.type %v", []eventstore.EventType{org.SAMLIDPChangedEventType, instance.SAMLIDPChangedEventType}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -2011,18 +2012,18 @@ func (p *idpTemplateProjection) reduceSAMLIDPChanged(event eventstore.Event) (*h SAMLCols := reduceSAMLIDPChangedColumns(idpEvent) if len(SAMLCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( SAMLCols, []handler.Condition{ handler.NewCond(SAMLIDCol, idpEvent.ID), handler.NewCond(SAMLInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateSAMLSuffix), + handler.WithTableSuffix(IDPTemplateSAMLSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -2042,9 +2043,9 @@ func (p *idpTemplateProjection) reduceAppleIDPAdded(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SFvg3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPAddedEventType /*, instance.AppleIDPAddedEventType*/}) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(IDPTemplateIDCol, idpEvent.ID), handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), @@ -2062,7 +2063,7 @@ func (p *idpTemplateProjection) reduceAppleIDPAdded(event eventstore.Event) (*ha handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(AppleIDCol, idpEvent.ID), handler.NewCol(AppleInstanceIDCol, idpEvent.Aggregate().InstanceID), @@ -2070,9 +2071,9 @@ func (p *idpTemplateProjection) reduceAppleIDPAdded(event eventstore.Event) (*ha handler.NewCol(AppleTeamIDCol, idpEvent.TeamID), handler.NewCol(AppleKeyIDCol, idpEvent.KeyID), handler.NewCol(ApplePrivateKeyCol, idpEvent.PrivateKey), - handler.NewCol(AppleScopesCol, database.StringArray(idpEvent.Scopes)), + handler.NewCol(AppleScopesCol, database.TextArray[string](idpEvent.Scopes)), }, - crdb.WithTableSuffix(IDPTemplateAppleSuffix), + handler.WithTableSuffix(IDPTemplateAppleSuffix), ), ), nil } @@ -2088,9 +2089,9 @@ func (p *idpTemplateProjection) reduceAppleIDPChanged(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-GBez3", "reduce.wrong.event.type %v", []eventstore.EventType{org.AppleIDPChangedEventType /*, instance.AppleIDPChangedEventType*/}) } - ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops := make([]func(eventstore.Event) handler.Exec, 0, 2) ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -2101,18 +2102,18 @@ func (p *idpTemplateProjection) reduceAppleIDPChanged(event eventstore.Event) (* appleCols := reduceAppleIDPChangedColumns(idpEvent) if len(appleCols) > 0 { ops = append(ops, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( appleCols, []handler.Condition{ handler.NewCond(AppleIDCol, idpEvent.ID), handler.NewCond(AppleInstanceIDCol, idpEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(IDPTemplateAppleSuffix), + handler.WithTableSuffix(IDPTemplateAppleSuffix), ), ) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( &idpEvent, ops..., ), nil @@ -2129,7 +2130,7 @@ func (p *idpTemplateProjection) reduceIDPConfigRemoved(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAFet", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &idpEvent, []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ConfigID), @@ -2149,7 +2150,7 @@ func (p *idpTemplateProjection) reduceIDPRemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-xbcvwin2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPRemovedEventType, instance.IDPRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( &idpEvent, []handler.Condition{ handler.NewCond(IDPTemplateIDCol, idpEvent.ID), @@ -2164,7 +2165,7 @@ func (p *idpTemplateProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Jp0D2K", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(IDPTemplateInstanceIDCol, e.Aggregate().InstanceID), @@ -2214,7 +2215,7 @@ func reduceOAuthIDPChangedColumns(idpEvent idp.OAuthIDPChangedEvent) []handler.C oauthCols = append(oauthCols, handler.NewCol(OAuthUserEndpointCol, *idpEvent.UserEndpoint)) } if idpEvent.Scopes != nil { - oauthCols = append(oauthCols, handler.NewCol(OAuthScopesCol, database.StringArray(idpEvent.Scopes))) + oauthCols = append(oauthCols, handler.NewCol(OAuthScopesCol, database.TextArray[string](idpEvent.Scopes))) } if idpEvent.IDAttribute != nil { oauthCols = append(oauthCols, handler.NewCol(OAuthIDAttributeCol, *idpEvent.IDAttribute)) @@ -2234,7 +2235,7 @@ func reduceOIDCIDPChangedColumns(idpEvent idp.OIDCIDPChangedEvent) []handler.Col oidcCols = append(oidcCols, handler.NewCol(OIDCIssuerCol, *idpEvent.Issuer)) } if idpEvent.Scopes != nil { - oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes))) + oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.TextArray[string](idpEvent.Scopes))) } if idpEvent.IsIDTokenMapping != nil { oidcCols = append(oidcCols, handler.NewCol(OIDCIDTokenMappingCol, *idpEvent.IsIDTokenMapping)) @@ -2268,7 +2269,7 @@ func reduceAzureADIDPChangedColumns(idpEvent idp.AzureADIDPChangedEvent) []handl azureADCols = append(azureADCols, handler.NewCol(AzureADClientSecretCol, *idpEvent.ClientSecret)) } if idpEvent.Scopes != nil { - azureADCols = append(azureADCols, handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes))) + azureADCols = append(azureADCols, handler.NewCol(AzureADScopesCol, database.TextArray[string](idpEvent.Scopes))) } if idpEvent.Tenant != nil { azureADCols = append(azureADCols, handler.NewCol(AzureADTenantCol, *idpEvent.Tenant)) @@ -2288,7 +2289,7 @@ func reduceGitHubIDPChangedColumns(idpEvent idp.GitHubIDPChangedEvent) []handler oauthCols = append(oauthCols, handler.NewCol(GitHubClientSecretCol, *idpEvent.ClientSecret)) } if idpEvent.Scopes != nil { - oauthCols = append(oauthCols, handler.NewCol(GitHubScopesCol, database.StringArray(idpEvent.Scopes))) + oauthCols = append(oauthCols, handler.NewCol(GitHubScopesCol, database.TextArray[string](idpEvent.Scopes))) } return oauthCols } @@ -2311,7 +2312,7 @@ func reduceGitHubEnterpriseIDPChangedColumns(idpEvent idp.GitHubEnterpriseIDPCha oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseUserEndpointCol, *idpEvent.UserEndpoint)) } if idpEvent.Scopes != nil { - oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseScopesCol, database.StringArray(idpEvent.Scopes))) + oauthCols = append(oauthCols, handler.NewCol(GitHubEnterpriseScopesCol, database.TextArray[string](idpEvent.Scopes))) } return oauthCols } @@ -2325,7 +2326,7 @@ func reduceGitLabIDPChangedColumns(idpEvent idp.GitLabIDPChangedEvent) []handler gitlabCols = append(gitlabCols, handler.NewCol(GitLabClientSecretCol, *idpEvent.ClientSecret)) } if idpEvent.Scopes != nil { - gitlabCols = append(gitlabCols, handler.NewCol(GitLabScopesCol, database.StringArray(idpEvent.Scopes))) + gitlabCols = append(gitlabCols, handler.NewCol(GitLabScopesCol, database.TextArray[string](idpEvent.Scopes))) } return gitlabCols } @@ -2342,7 +2343,7 @@ func reduceGitLabSelfHostedIDPChangedColumns(idpEvent idp.GitLabSelfHostedIDPCha gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedClientSecretCol, *idpEvent.ClientSecret)) } if idpEvent.Scopes != nil { - gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedScopesCol, database.StringArray(idpEvent.Scopes))) + gitlabCols = append(gitlabCols, handler.NewCol(GitLabSelfHostedScopesCol, database.TextArray[string](idpEvent.Scopes))) } return gitlabCols } @@ -2356,7 +2357,7 @@ func reduceGoogleIDPChangedColumns(idpEvent idp.GoogleIDPChangedEvent) []handler googleCols = append(googleCols, handler.NewCol(GoogleClientSecretCol, *idpEvent.ClientSecret)) } if idpEvent.Scopes != nil { - googleCols = append(googleCols, handler.NewCol(GoogleScopesCol, database.StringArray(idpEvent.Scopes))) + googleCols = append(googleCols, handler.NewCol(GoogleScopesCol, database.TextArray[string](idpEvent.Scopes))) } return googleCols } @@ -2364,7 +2365,7 @@ func reduceGoogleIDPChangedColumns(idpEvent idp.GoogleIDPChangedEvent) []handler func reduceLDAPIDPChangedColumns(idpEvent idp.LDAPIDPChangedEvent) []handler.Column { ldapCols := make([]handler.Column, 0, 22) if idpEvent.Servers != nil { - ldapCols = append(ldapCols, handler.NewCol(LDAPServersCol, database.StringArray(idpEvent.Servers))) + ldapCols = append(ldapCols, handler.NewCol(LDAPServersCol, database.TextArray[string](idpEvent.Servers))) } if idpEvent.StartTLS != nil { ldapCols = append(ldapCols, handler.NewCol(LDAPStartTLSCol, *idpEvent.StartTLS)) @@ -2382,10 +2383,10 @@ func reduceLDAPIDPChangedColumns(idpEvent idp.LDAPIDPChangedEvent) []handler.Col ldapCols = append(ldapCols, handler.NewCol(LDAPUserBaseCol, *idpEvent.UserBase)) } if idpEvent.UserObjectClasses != nil { - ldapCols = append(ldapCols, handler.NewCol(LDAPUserObjectClassesCol, database.StringArray(idpEvent.UserObjectClasses))) + ldapCols = append(ldapCols, handler.NewCol(LDAPUserObjectClassesCol, database.TextArray[string](idpEvent.UserObjectClasses))) } if idpEvent.UserFilters != nil { - ldapCols = append(ldapCols, handler.NewCol(LDAPUserFiltersCol, database.StringArray(idpEvent.UserFilters))) + ldapCols = append(ldapCols, handler.NewCol(LDAPUserFiltersCol, database.TextArray[string](idpEvent.UserFilters))) } if idpEvent.Timeout != nil { ldapCols = append(ldapCols, handler.NewCol(LDAPTimeoutCol, *idpEvent.Timeout)) @@ -2447,7 +2448,7 @@ func reduceAppleIDPChangedColumns(idpEvent idp.AppleIDPChangedEvent) []handler.C appleCols = append(appleCols, handler.NewCol(ApplePrivateKeyCol, *idpEvent.PrivateKey)) } if idpEvent.Scopes != nil { - appleCols = append(appleCols, handler.NewCol(AppleScopesCol, database.StringArray(idpEvent.Scopes))) + appleCols = append(appleCols, handler.NewCol(AppleScopesCol, database.TextArray[string](idpEvent.Scopes))) } return appleCols } diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index 9e99c6cdb..5e33aebe8 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -9,8 +9,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -38,17 +37,17 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(IDPInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -65,16 +64,16 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { name: "org reduceOwnerRemoved", reduce: (&idpTemplateProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -92,18 +91,18 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { name: "org reduceIDPRemoved", reduce: (&idpTemplateProjection{}).reduceIDPRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPRemovedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id" }`), - ), org.IDPRemovedEventMapper), + ), org.IDPRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -121,18 +120,18 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { name: "org reduceIDPConfigRemoved", reduce: (&idpTemplateProjection{}).reduceIDPConfigRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-id" }`), - ), org.IDPConfigRemovedEventMapper), + ), org.IDPConfigRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -175,10 +174,11 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { { name: "instance reduceOAuthIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OAuthIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OAuthIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "clientId": "client_id", @@ -197,13 +197,12 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.OAuthIDPAddedEventMapper), + ), instance.OAuthIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOAuthIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -235,7 +234,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "id-attribute", }, }, @@ -246,10 +245,11 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { { name: "org reduceOAuthIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OAuthIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.OAuthIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "clientId": "client_id", @@ -268,13 +268,12 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.OAuthIDPAddedEventMapper), + ), org.OAuthIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOAuthIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -306,7 +305,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "id-attribute", }, }, @@ -317,21 +316,21 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { { name: "instance reduceOAuthIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OAuthIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OAuthIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "clientId": "id" }`), - ), instance.OAuthIDPChangedEventMapper), + ), instance.OAuthIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOAuthIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -359,10 +358,11 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { { name: "instance reduceOAuthIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OAuthIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OAuthIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "clientId": "client_id", @@ -381,13 +381,12 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.OAuthIDPChangedEventMapper), + ), instance.OAuthIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOAuthIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -412,7 +411,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "id-attribute", "idp-id", "instance-id", @@ -451,10 +450,11 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { { name: "instance reduceAzureADIDPAdded minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.AzureADIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.AzureADIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "client_id": "client_id", @@ -464,13 +464,12 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "keyId": "key-id" } }`), - ), instance.AzureADIDPAddedEventMapper), + ), instance.AzureADIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -499,7 +498,7 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray(nil), + database.TextArray[string](nil), "", false, }, @@ -511,10 +510,11 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { { name: "instance reduceAzureADIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.AzureADIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.AzureADIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "client_id": "client_id", @@ -531,13 +531,12 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.AzureADIDPAddedEventMapper), + ), instance.AzureADIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -566,7 +565,7 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "tenant", true, }, @@ -578,10 +577,11 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { { name: "org reduceAzureADIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.AzureADIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.AzureADIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "client_id": "client_id", @@ -598,13 +598,12 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.AzureADIDPAddedEventMapper), + ), org.AzureADIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -633,7 +632,7 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "tenant", true, }, @@ -645,21 +644,21 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { { name: "instance reduceAzureADIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.AzureADIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.AzureADIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "client_id": "id" }`), - ), instance.AzureADIDPChangedEventMapper), + ), instance.AzureADIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceAzureADIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -687,10 +686,11 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { { name: "instance reduceAzureADIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.AzureADIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.AzureADIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "client_id": "client_id", @@ -707,13 +707,12 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.AzureADIDPChangedEventMapper), + ), instance.AzureADIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceAzureADIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -735,7 +734,7 @@ func TestIDPTemplateProjection_reducesAzureAD(t *testing.T) { expectedArgs: []interface{}{ "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "tenant", true, "idp-id", @@ -775,10 +774,11 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { { name: "instance reduceGitHubIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -793,13 +793,12 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitHubIDPAddedEventMapper), + ), instance.GitHubIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -828,7 +827,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -838,10 +837,11 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { { name: "org reduceGitHubIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.GitHubIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.GitHubIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -856,13 +856,12 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.GitHubIDPAddedEventMapper), + ), org.GitHubIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -891,7 +890,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -901,21 +900,21 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { { name: "instance reduceGitHubIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "clientId": "id" }`), - ), instance.GitHubIDPChangedEventMapper), + ), instance.GitHubIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -943,10 +942,11 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { { name: "instance reduceGitHubIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -961,13 +961,12 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitHubIDPChangedEventMapper), + ), instance.GitHubIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -989,7 +988,7 @@ func TestIDPTemplateProjection_reducesGitHub(t *testing.T) { expectedArgs: []interface{}{ "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-id", "instance-id", }, @@ -1027,10 +1026,11 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { { name: "instance reduceGitHubEnterpriseIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubEnterpriseIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubEnterpriseIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -1048,13 +1048,12 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitHubEnterpriseIDPAddedEventMapper), + ), instance.GitHubEnterpriseIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1086,7 +1085,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1096,10 +1095,11 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { { name: "org reduceGitHubEnterpriseIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.GitHubEnterpriseIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.GitHubEnterpriseIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -1117,13 +1117,12 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.GitHubEnterpriseIDPAddedEventMapper), + ), org.GitHubEnterpriseIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1155,7 +1154,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1165,21 +1164,21 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { { name: "instance reduceGitHubEnterpriseIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubEnterpriseIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubEnterpriseIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "clientId": "id" }`), - ), instance.GitHubEnterpriseIDPChangedEventMapper), + ), instance.GitHubEnterpriseIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1207,10 +1206,11 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { { name: "instance reduceGitHubEnterpriseIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitHubEnterpriseIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitHubEnterpriseIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -1228,13 +1228,12 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitHubEnterpriseIDPChangedEventMapper), + ), instance.GitHubEnterpriseIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitHubEnterpriseIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1259,7 +1258,7 @@ func TestIDPTemplateProjection_reducesGitHubEnterprise(t *testing.T) { "auth", "token", "user", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-id", "instance-id", }, @@ -1297,10 +1296,11 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { { name: "instance reduceGitLabIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "client_id": "client_id", "client_secret": { @@ -1314,13 +1314,12 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitLabIDPAddedEventMapper), + ), instance.GitLabIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1349,7 +1348,7 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1359,10 +1358,11 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { { name: "org reduceGitLabIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.GitLabIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.GitLabIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "client_id": "client_id", "client_secret": { @@ -1376,13 +1376,12 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.GitLabIDPAddedEventMapper), + ), org.GitLabIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1411,7 +1410,7 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1421,21 +1420,21 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { { name: "instance reduceGitLabIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "client_id": "id" }`), - ), instance.GitLabIDPChangedEventMapper), + ), instance.GitLabIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1463,10 +1462,11 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { { name: "instance reduceGitLabIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "client_id": "client_id", @@ -1481,13 +1481,12 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitLabIDPChangedEventMapper), + ), instance.GitLabIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1509,7 +1508,7 @@ func TestIDPTemplateProjection_reducesGitLab(t *testing.T) { expectedArgs: []interface{}{ "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-id", "instance-id", }, @@ -1547,10 +1546,11 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { { name: "instance reduceGitLabSelfHostedIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabSelfHostedIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabSelfHostedIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "issuer": "issuer", @@ -1566,13 +1566,12 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitLabSelfHostedIDPAddedEventMapper), + ), instance.GitLabSelfHostedIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1602,7 +1601,7 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "issuer", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1612,10 +1611,11 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { { name: "org reduceGitLabSelfHostedIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.GitLabSelfHostedIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.GitLabSelfHostedIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "issuer": "issuer", @@ -1631,13 +1631,12 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.GitLabSelfHostedIDPAddedEventMapper), + ), org.GitLabSelfHostedIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1667,7 +1666,7 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "issuer", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1677,21 +1676,21 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { { name: "instance reduceGitLabSelfHostedIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabSelfHostedIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabSelfHostedIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "issuer": "issuer" }`), - ), instance.GitLabSelfHostedIDPChangedEventMapper), + ), instance.GitLabSelfHostedIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1719,10 +1718,11 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { { name: "instance reduceGitLabSelfHostedIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GitLabSelfHostedIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GitLabSelfHostedIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "issuer": "issuer", @@ -1738,13 +1738,12 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GitLabSelfHostedIDPChangedEventMapper), + ), instance.GitLabSelfHostedIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGitLabSelfHostedIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1767,7 +1766,7 @@ func TestIDPTemplateProjection_reducesGitLabSelfHosted(t *testing.T) { "issuer", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-id", "instance-id", }, @@ -1805,10 +1804,11 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { { name: "instance reduceGoogleIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GoogleIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GoogleIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "clientId": "client_id", "clientSecret": { @@ -1822,13 +1822,12 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GoogleIDPAddedEventMapper), + ), instance.GoogleIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGoogleIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1857,7 +1856,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1867,10 +1866,11 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { { name: "org reduceGoogleIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.GoogleIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.GoogleIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "clientId": "client_id", "clientSecret": { @@ -1884,13 +1884,12 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.GoogleIDPAddedEventMapper), + ), org.GoogleIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGoogleIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1919,7 +1918,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -1929,21 +1928,21 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { { name: "instance reduceGoogleIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GoogleIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GoogleIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "clientId": "id" }`), - ), instance.GoogleIDPChangedEventMapper), + ), instance.GoogleIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGoogleIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1971,10 +1970,11 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { { name: "instance reduceGoogleIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.GoogleIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.GoogleIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "clientId": "client_id", @@ -1989,13 +1989,12 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.GoogleIDPChangedEventMapper), + ), instance.GoogleIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceGoogleIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2017,7 +2016,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { expectedArgs: []interface{}{ "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-id", "instance-id", }, @@ -2055,10 +2054,11 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { { name: "instance reduceLDAPIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LDAPIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LDAPIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "servers": ["server"], @@ -2092,13 +2092,12 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.LDAPIDPAddedEventMapper), + ), instance.LDAPIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceLDAPIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2125,14 +2124,14 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { expectedArgs: []interface{}{ "idp-id", "instance-id", - database.StringArray{"server"}, + database.TextArray[string]{"server"}, false, "basedn", "binddn", anyArg{}, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -2156,10 +2155,11 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { { name: "org reduceLDAPIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LDAPIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LDAPIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "servers": ["server"], @@ -2193,13 +2193,12 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.LDAPIDPAddedEventMapper), + ), org.LDAPIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceLDAPIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2226,14 +2225,14 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { expectedArgs: []interface{}{ "idp-id", "instance-id", - database.StringArray{"server"}, + database.TextArray[string]{"server"}, false, "basedn", "binddn", anyArg{}, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -2257,21 +2256,21 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { { name: "instance reduceLDAPIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LDAPIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LDAPIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "baseDN": "basedn" }`), - ), instance.LDAPIDPChangedEventMapper), + ), instance.LDAPIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceLDAPIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2299,10 +2298,11 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { { name: "instance reduceLDAPIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LDAPIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LDAPIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", "servers": ["server"], @@ -2336,13 +2336,12 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.LDAPIDPChangedEventMapper), + ), instance.LDAPIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceLDAPIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2362,14 +2361,14 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { { expectedStmt: "UPDATE projections.idp_templates5_ldap2 SET (servers, start_tls, base_dn, bind_dn, bind_password, user_base, user_object_classes, user_filters, timeout, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) WHERE (idp_id = $23) AND (instance_id = $24)", expectedArgs: []interface{}{ - database.StringArray{"server"}, + database.TextArray[string]{"server"}, false, "basedn", "binddn", anyArg{}, "user", - database.StringArray{"object"}, - database.StringArray{"filter"}, + database.TextArray[string]{"object"}, + database.TextArray[string]{"filter"}, time.Duration(30000000000), "id", "first", @@ -2396,16 +2395,16 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&idpProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2449,7 +2448,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { name: "instance reduceAppleIDPAdded", args: args{ event: getEvent(testEvent( - repository.EventType(instance.AppleIDPAddedEventType), + instance.AppleIDPAddedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2471,9 +2470,8 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceAppleIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2504,7 +2502,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { "team_id", "key_id", anyArg{}, - database.StringArray{"name"}, + database.TextArray[string]{"name"}, }, }, }, @@ -2515,7 +2513,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { name: "org reduceAppleIDPAdded", args: args{ event: getEvent(testEvent( - repository.EventType(org.AppleIDPAddedEventType), + org.AppleIDPAddedEventType, org.AggregateType, []byte(`{ "id": "idp-id", @@ -2537,9 +2535,8 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceAppleIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2570,7 +2567,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { "team_id", "key_id", anyArg{}, - database.StringArray{"name"}, + database.TextArray[string]{"name"}, }, }, }, @@ -2581,7 +2578,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { name: "instance reduceAppleIDPChanged minimal", args: args{ event: getEvent(testEvent( - repository.EventType(instance.AppleIDPChangedEventType), + instance.AppleIDPChangedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2592,9 +2589,8 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceAppleIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2623,7 +2619,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { name: "instance reduceAppleIDPChanged", args: args{ event: getEvent(testEvent( - repository.EventType(instance.AppleIDPChangedEventType), + instance.AppleIDPChangedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2646,9 +2642,8 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceAppleIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2672,7 +2667,7 @@ func TestIDPTemplateProjection_reducesApple(t *testing.T) { "team_id", "key_id", anyArg{}, - database.StringArray{"name"}, + database.TextArray[string]{"name"}, "idp-id", "instance-id", }, @@ -2711,7 +2706,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { name: "instance reduceSAMLIDPAdded", args: args{ event: getEvent(testEvent( - repository.EventType(instance.SAMLIDPAddedEventType), + instance.SAMLIDPAddedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2734,9 +2729,8 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceSAMLIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2778,7 +2772,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { name: "org reduceSAMLIDPAdded", args: args{ event: getEvent(testEvent( - repository.EventType(org.SAMLIDPAddedEventType), + org.SAMLIDPAddedEventType, org.AggregateType, []byte(`{ "id": "idp-id", @@ -2801,9 +2795,8 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceSAMLIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2845,7 +2838,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { name: "instance reduceSAMLIDPChanged minimal", args: args{ event: getEvent(testEvent( - repository.EventType(instance.SAMLIDPChangedEventType), + instance.SAMLIDPChangedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2856,9 +2849,8 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceSAMLIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2887,7 +2879,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { name: "instance reduceSAMLIDPChanged", args: args{ event: getEvent(testEvent( - repository.EventType(instance.SAMLIDPChangedEventType), + instance.SAMLIDPChangedEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -2910,9 +2902,8 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceSAMLIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -2950,15 +2941,14 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { reduce: (&idpProjection{}).reduceOwnerRemoved, args: args{ event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), + org.OrgRemovedEventType, org.AggregateType, nil, ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3001,10 +2991,11 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { { name: "instance reduceOIDCIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OIDCIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OIDCIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "issuer": "issuer", "clientId": "client_id", @@ -3020,13 +3011,12 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.OIDCIDPAddedEventMapper), + ), instance.OIDCIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3056,7 +3046,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "issuer", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, }, }, @@ -3067,10 +3057,11 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { { name: "org reduceOIDCIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OIDCIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.OIDCIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "issuer": "issuer", "clientId": "client_id", @@ -3086,13 +3077,12 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.OIDCIDPAddedEventMapper), + ), org.OIDCIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3122,7 +3112,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "issuer", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, }, }, @@ -3133,21 +3123,21 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { { name: "instance reduceOIDCIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OIDCIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OIDCIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "clientId": "id" }`), - ), instance.OIDCIDPChangedEventMapper), + ), instance.OIDCIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3175,10 +3165,11 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { { name: "instance reduceOIDCIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OIDCIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.OIDCIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "name": "name", "issuer": "issuer", @@ -3195,13 +3186,12 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.OIDCIDPChangedEventMapper), + ), instance.OIDCIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3224,7 +3214,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "client_id", anyArg{}, "issuer", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, "idp-id", "instance-id", @@ -3238,7 +3228,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { name: "instance reduceOIDCIDPMigratedAzureAD", args: args{ event: getEvent(testEvent( - repository.EventType(instance.OIDCIDPMigratedAzureADEventType), + instance.OIDCIDPMigratedAzureADEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -3261,9 +3251,8 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedAzureAD, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3295,7 +3284,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "tenant", true, }, @@ -3308,7 +3297,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { name: "org reduceOIDCIDPMigratedAzureAD", args: args{ event: getEvent(testEvent( - repository.EventType(org.OIDCIDPMigratedAzureADEventType), + org.OIDCIDPMigratedAzureADEventType, org.AggregateType, []byte(`{ "id": "idp-id", @@ -3331,9 +3320,8 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedAzureAD, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3365,7 +3353,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "tenant", true, }, @@ -3378,7 +3366,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { name: "instance reduceOIDCIDPMigratedGoogle", args: args{ event: getEvent(testEvent( - repository.EventType(instance.OIDCIDPMigratedGoogleEventType), + instance.OIDCIDPMigratedGoogleEventType, instance.AggregateType, []byte(`{ "id": "idp-id", @@ -3399,9 +3387,8 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedGoogle, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3433,7 +3420,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -3444,7 +3431,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { name: "org reduceOIDCIDPMigratedGoogle", args: args{ event: getEvent(testEvent( - repository.EventType(org.OIDCIDPMigratedGoogleEventType), + org.OIDCIDPMigratedGoogleEventType, org.AggregateType, []byte(`{ "id": "idp-id", @@ -3465,9 +3452,8 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { }, reduce: (&idpTemplateProjection{}).reduceOIDCIDPMigratedGoogle, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3499,7 +3485,7 @@ func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { "instance-id", "client_id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, }, }, }, @@ -3535,23 +3521,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "idpType": 0, "stylingType": 0, "autoRegister": true }`), - ), instance.IDPConfigAddedEventMapper), + ), instance.IDPConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3580,23 +3566,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "idpType": 0, "stylingType": 0, "autoRegister": true }`), - ), org.IDPConfigAddedEventMapper), + ), org.IDPConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3625,22 +3611,22 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "stylingType": 1, "autoRegister": true }`), - ), instance.IDPConfigChangedEventMapper), + ), instance.IDPConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3661,22 +3647,22 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "stylingType": 1, "autoRegister": true }`), - ), org.IDPConfigChangedEventMapper), + ), org.IDPConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3697,10 +3683,11 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldOIDCConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPOIDCConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPOIDCConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -3713,13 +3700,12 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), instance.IDPOIDCConfigAddedEventMapper), + ), instance.IDPOIDCConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3740,7 +3726,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "issuer", "client-id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, }, }, @@ -3751,10 +3737,11 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldOIDCConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPOIDCConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPOIDCConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -3767,13 +3754,12 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), org.IDPOIDCConfigAddedEventMapper), + ), org.IDPOIDCConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3794,7 +3780,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "issuer", "client-id", anyArg{}, - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, true, }, }, @@ -3805,10 +3791,11 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldOIDCConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPOIDCConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPOIDCConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -3821,13 +3808,12 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), instance.IDPOIDCConfigChangedEventMapper), + ), instance.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3845,7 +3831,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "client-id", anyArg{}, "issuer", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-config-id", "instance-id", }, @@ -3857,10 +3843,11 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldOIDCConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPOIDCConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPOIDCConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -3873,13 +3860,12 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), org.IDPOIDCConfigChangedEventMapper), + ), org.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3897,7 +3883,7 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { "client-id", anyArg{}, "issuer", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, "idp-config-id", "instance-id", }, @@ -3909,23 +3895,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldJWTConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPJWTConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPJWTConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), instance.IDPJWTConfigAddedEventMapper), + ), instance.IDPJWTConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldJWTConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -3957,23 +3943,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldJWTConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPJWTConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPJWTConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), org.IDPJWTConfigAddedEventMapper), + ), org.IDPJWTConfigAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldJWTConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4004,23 +3990,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "instance reduceOldJWTConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPJWTConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPJWTConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), instance.IDPJWTConfigChangedEventMapper), + ), instance.IDPJWTConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4050,23 +4036,23 @@ func TestIDPTemplateProjection_reducesOldConfig(t *testing.T) { { name: "org reduceOldJWTConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPJWTConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPJWTConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), org.IDPJWTConfigChangedEventMapper), + ), org.IDPJWTConfigChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceOldJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4122,10 +4108,11 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { { name: "instance reduceJWTIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.JWTIDPAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.JWTIDPAddedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "issuer": "issuer", "jwtEndpoint": "jwt", @@ -4136,13 +4123,12 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.JWTIDPAddedEventMapper), + ), instance.JWTIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceJWTIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4182,10 +4168,11 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { { name: "org reduceJWTIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.JWTIDPAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.JWTIDPAddedEventType, + org.AggregateType, + []byte(`{ "id": "idp-id", "issuer": "issuer", "jwtEndpoint": "jwt", @@ -4196,13 +4183,12 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), org.JWTIDPAddedEventMapper), + ), org.JWTIDPAddedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceJWTIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4242,21 +4228,21 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { { name: "instance reduceJWTIDPChanged minimal", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.JWTIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.JWTIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "isCreationAllowed": true, "jwtEndpoint": "jwt" }`), - ), instance.JWTIDPChangedEventMapper), + ), instance.JWTIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceJWTIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -4284,10 +4270,11 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { { name: "instance reduceJWTIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.JWTIDPChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.JWTIDPChangedEventType, + instance.AggregateType, + []byte(`{ "id": "idp-id", "issuer": "issuer", "jwtEndpoint": "jwt", @@ -4298,13 +4285,12 @@ func TestIDPTemplateProjection_reducesJWT(t *testing.T) { "isAutoCreation": true, "isAutoUpdate": true }`), - ), instance.JWTIDPChangedEventMapper), + ), instance.JWTIDPChangedEventMapper), }, reduce: (&idpTemplateProjection{}).reduceJWTIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/idp_test.go b/internal/query/projection/idp_test.go index 54eda3b84..bedd528cd 100644 --- a/internal/query/projection/idp_test.go +++ b/internal/query/projection/idp_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -26,23 +25,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "idpType": 0, "stylingType": 0, "autoRegister": true }`), - ), instance.IDPConfigAddedEventMapper), + ), instance.IDPConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -68,22 +67,22 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "stylingType": 1, "autoRegister": true }`), - ), instance.IDPConfigChangedEventMapper), + ), instance.IDPConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -105,19 +104,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceIDPDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigDeactivatedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigDeactivatedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), instance.IDPConfigDeactivatedEventMapper), + ), instance.IDPConfigDeactivatedEventMapper), }, reduce: (&idpProjection{}).reduceIDPDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -137,19 +136,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceIDPReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigReactivatedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigReactivatedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), instance.IDPConfigReactivatedEventMapper), + ), instance.IDPConfigReactivatedEventMapper), }, reduce: (&idpProjection{}).reduceIDPReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -169,19 +168,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceIDPRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigRemovedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), instance.IDPConfigRemovedEventMapper), + ), instance.IDPConfigRemovedEventMapper), }, reduce: (&idpProjection{}).reduceIDPRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -198,17 +197,17 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(IDPInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -224,10 +223,11 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceOIDCConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPOIDCConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPOIDCConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -242,13 +242,12 @@ func TestIDPProjection_reduces(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), instance.IDPOIDCConfigAddedEventMapper), + ), instance.IDPOIDCConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -269,7 +268,7 @@ func TestIDPProjection_reduces(t *testing.T) { "client-id", anyArg{}, "issuer", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "https://api.zitadel.ch/authorize", @@ -283,10 +282,11 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceOIDCConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPOIDCConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPOIDCConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -301,13 +301,12 @@ func TestIDPProjection_reduces(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), instance.IDPOIDCConfigChangedEventMapper), + ), instance.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -327,7 +326,7 @@ func TestIDPProjection_reduces(t *testing.T) { "issuer", "https://api.zitadel.ch/authorize", "https://api.zitadel.ch/token", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "idp-config-id", @@ -341,17 +340,17 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceOIDCConfigChanged: no op", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPOIDCConfigChangedEventType), - instance.AggregateType, - []byte("{}"), - ), instance.IDPOIDCConfigChangedEventMapper), + event: getEvent( + testEvent( + instance.IDPOIDCConfigChangedEventType, + instance.AggregateType, + []byte("{}"), + ), instance.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -360,23 +359,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceJWTConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPJWTConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPJWTConfigAddedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), instance.IDPJWTConfigAddedEventMapper), + ), instance.IDPJWTConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -407,23 +406,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceJWTConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPJWTConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPJWTConfigChangedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), instance.IDPJWTConfigChangedEventMapper), + ), instance.IDPJWTConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -453,17 +452,17 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "instance reduceJWTConfigChanged: no op", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPJWTConfigChangedEventType), - instance.AggregateType, - []byte(`{}`), - ), instance.IDPJWTConfigChangedEventMapper), + event: getEvent( + testEvent( + instance.IDPJWTConfigChangedEventType, + instance.AggregateType, + []byte(`{}`), + ), instance.IDPJWTConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -472,23 +471,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceIDPAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "idpType": 0, "stylingType": 0, "autoRegister": true }`), - ), org.IDPConfigAddedEventMapper), + ), org.IDPConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceIDPAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -514,22 +513,22 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceIDPChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "name": "custom-zitadel-instance", "stylingType": 1, "autoRegister": true }`), - ), org.IDPConfigChangedEventMapper), + ), org.IDPConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceIDPChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -551,19 +550,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceIDPDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigDeactivatedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigDeactivatedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), org.IDPConfigDeactivatedEventMapper), + ), org.IDPConfigDeactivatedEventMapper), }, reduce: (&idpProjection{}).reduceIDPDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -583,19 +582,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceIDPReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigReactivatedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigReactivatedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), org.IDPConfigReactivatedEventMapper), + ), org.IDPConfigReactivatedEventMapper), }, reduce: (&idpProjection{}).reduceIDPReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -615,19 +614,19 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceIDPRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), org.IDPConfigRemovedEventMapper), + ), org.IDPConfigRemovedEventMapper), }, reduce: (&idpProjection{}).reduceIDPRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -644,10 +643,11 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceOIDCConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPOIDCConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPOIDCConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -662,13 +662,12 @@ func TestIDPProjection_reduces(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), org.IDPOIDCConfigAddedEventMapper), + ), org.IDPOIDCConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -689,7 +688,7 @@ func TestIDPProjection_reduces(t *testing.T) { "client-id", anyArg{}, "issuer", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "https://api.zitadel.ch/authorize", @@ -703,10 +702,11 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceOIDCConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPOIDCConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPOIDCConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "clientId": "client-id", "clientSecret": { @@ -721,13 +721,12 @@ func TestIDPProjection_reduces(t *testing.T) { "idpDisplayNameMapping": 0, "usernameMapping": 1 }`), - ), org.IDPOIDCConfigChangedEventMapper), + ), org.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -747,7 +746,7 @@ func TestIDPProjection_reduces(t *testing.T) { "issuer", "https://api.zitadel.ch/authorize", "https://api.zitadel.ch/token", - database.StringArray{"profile"}, + database.TextArray[string]{"profile"}, domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "idp-config-id", @@ -761,17 +760,17 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceOIDCConfigChanged: no op", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPOIDCConfigChangedEventType), - org.AggregateType, - []byte("{}"), - ), org.IDPOIDCConfigChangedEventMapper), + event: getEvent( + testEvent( + org.IDPOIDCConfigChangedEventType, + org.AggregateType, + []byte("{}"), + ), org.IDPOIDCConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceOIDCConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -780,23 +779,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceJWTConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPJWTConfigAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPJWTConfigAddedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), org.IDPJWTConfigAddedEventMapper), + ), org.IDPJWTConfigAddedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -827,23 +826,23 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceJWTConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPJWTConfigChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPJWTConfigChangedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "jwtEndpoint": "https://api.zitadel.ch/jwt", "issuer": "issuer", "keysEndpoint": "https://api.zitadel.ch/keys", "headerName": "hodor" }`), - ), org.IDPJWTConfigChangedEventMapper), + ), org.IDPJWTConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -873,17 +872,17 @@ func TestIDPProjection_reduces(t *testing.T) { { name: "org reduceJWTConfigChanged: no op", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPJWTConfigChangedEventType), - org.AggregateType, - []byte(`{}`), - ), org.IDPJWTConfigChangedEventMapper), + event: getEvent( + testEvent( + org.IDPJWTConfigChangedEventType, + org.AggregateType, + []byte(`{}`), + ), org.IDPJWTConfigChangedEventMapper), }, reduce: (&idpProjection{}).reduceJWTConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -893,16 +892,16 @@ func TestIDPProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&idpProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/idp_user_link.go b/internal/query/projection/idp_user_link.go index c4b15a36d..ce588a20e 100644 --- a/internal/query/projection/idp_user_link.go +++ b/internal/query/projection/idp_user_link.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -26,41 +26,42 @@ const ( IDPUserLinkOwnerRemovedCol = "owner_removed" ) -type idpUserLinkProjection struct { - crdb.StatementHandler +type idpUserLinkProjection struct{} + +func newIDPUserLinkProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(idpUserLinkProjection)) } -func newIDPUserLinkProjection(ctx context.Context, config crdb.StatementHandlerConfig) *idpUserLinkProjection { - p := new(idpUserLinkProjection) - config.ProjectionName = IDPUserLinkTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(IDPUserLinkIDPIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkExternalUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPUserLinkChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(IDPUserLinkSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(IDPUserLinkResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkDisplayNameCol, crdb.ColumnTypeText), - crdb.NewColumn(IDPUserLinkOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*idpUserLinkProjection) Name() string { + return IDPUserLinkTable +} + +func (*idpUserLinkProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(IDPUserLinkIDPIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkUserIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkExternalUserIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPUserLinkChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(IDPUserLinkSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(IDPUserLinkResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkDisplayNameCol, handler.ColumnTypeText), + handler.NewColumn(IDPUserLinkOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(IDPUserLinkInstanceIDCol, IDPUserLinkIDPIDCol, IDPUserLinkExternalUserIDCol), - crdb.WithIndex(crdb.NewIndex("user_id", []string{IDPUserLinkUserIDCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{IDPUserLinkOwnerRemovedCol})), + handler.NewPrimaryKey(IDPUserLinkInstanceIDCol, IDPUserLinkIDPIDCol, IDPUserLinkExternalUserIDCol), + handler.WithIndex(handler.NewIndex("user_id", []string{IDPUserLinkUserIDCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{IDPUserLinkOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *idpUserLinkProjection) reducers() []handler.AggregateReducer { +func (p *idpUserLinkProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserIDPLinkAddedType, Reduce: p.reduceAdded, @@ -85,7 +86,7 @@ func (p *idpUserLinkProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.IDPConfigRemovedEventType, Reduce: p.reduceIDPConfigRemoved, @@ -98,7 +99,7 @@ func (p *idpUserLinkProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.IDPConfigRemovedEventType, Reduce: p.reduceIDPConfigRemoved, @@ -118,7 +119,7 @@ func (p *idpUserLinkProjection) reduceAdded(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DpmXq", "reduce.wrong.event.type %s", user.UserIDPLinkAddedType) } - return crdb.NewCreateStatement(e, + return handler.NewCreateStatement(e, []handler.Column{ handler.NewCol(IDPUserLinkIDPIDCol, e.IDPConfigID), handler.NewCol(IDPUserLinkUserIDCol, e.Aggregate().ID), @@ -139,7 +140,7 @@ func (p *idpUserLinkProjection) reduceRemoved(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-AZmfJ", "reduce.wrong.event.type %s", user.UserIDPLinkRemovedType) } - return crdb.NewDeleteStatement(e, + return handler.NewDeleteStatement(e, []handler.Condition{ handler.NewCond(IDPUserLinkIDPIDCol, e.IDPConfigID), handler.NewCond(IDPUserLinkUserIDCol, e.Aggregate().ID), @@ -155,7 +156,7 @@ func (p *idpUserLinkProjection) reduceCascadeRemoved(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-jQpv9", "reduce.wrong.event.type %s", user.UserIDPLinkCascadeRemovedType) } - return crdb.NewDeleteStatement(e, + return handler.NewDeleteStatement(e, []handler.Condition{ handler.NewCond(IDPUserLinkIDPIDCol, e.IDPConfigID), handler.NewCond(IDPUserLinkUserIDCol, e.Aggregate().ID), @@ -171,7 +172,7 @@ func (p *idpUserLinkProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-PGiAY", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(IDPUserLinkResourceOwnerCol, e.Aggregate().ID), @@ -186,7 +187,7 @@ func (p *idpUserLinkProjection) reduceUserRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-uwlWE", "reduce.wrong.event.type %s", user.UserRemovedType) } - return crdb.NewDeleteStatement(e, + return handler.NewDeleteStatement(e, []handler.Condition{ handler.NewCond(IDPUserLinkUserIDCol, e.Aggregate().ID), handler.NewCond(IDPUserLinkInstanceIDCol, e.Aggregate().InstanceID), @@ -200,7 +201,7 @@ func (p *idpUserLinkProjection) reduceExternalIDMigrated(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-AS3th", "reduce.wrong.event.type %s", user.UserIDPExternalIDMigratedType) } - return crdb.NewUpdateStatement(e, + return handler.NewUpdateStatement(e, []handler.Column{ handler.NewCol(IDPUserLinkChangeDateCol, e.CreationDate()), handler.NewCol(IDPUserLinkSequenceCol, e.Sequence()), @@ -227,7 +228,7 @@ func (p *idpUserLinkProjection) reduceIDPConfigRemoved(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-iCKSj", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) } - return crdb.NewDeleteStatement(event, + return handler.NewDeleteStatement(event, []handler.Condition{ handler.NewCond(IDPUserLinkIDPIDCol, idpID), handler.NewCond(IDPUserLinkResourceOwnerCol, event.Aggregate().ResourceOwner), diff --git a/internal/query/projection/idp_user_link_test.go b/internal/query/projection/idp_user_link_test.go index f518163e2..a5e860844 100644 --- a/internal/query/projection/idp_user_link_test.go +++ b/internal/query/projection/idp_user_link_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -25,21 +24,21 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserIDPLinkAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserIDPLinkAddedType, + user.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "userId": "external-user-id", "displayName": "gigi@caos.ch" }`), - ), user.UserIDPLinkAddedEventMapper), + ), user.UserIDPLinkAddedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -63,20 +62,20 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "reduceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserIDPLinkRemovedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserIDPLinkRemovedType, + user.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "userId": "external-user-id" }`), - ), user.UserIDPLinkRemovedEventMapper), + ), user.UserIDPLinkRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -95,20 +94,20 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "reduceCascadeRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserIDPLinkCascadeRemovedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserIDPLinkCascadeRemovedType, + user.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id", "userId": "external-user-id" }`), - ), user.UserIDPLinkCascadeRemovedEventMapper), + ), user.UserIDPLinkCascadeRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -127,17 +126,17 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "reduceOwnerRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - []byte(`{}`), - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + []byte(`{}`), + ), org.OrgRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -154,17 +153,17 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(IDPUserLinkInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -180,17 +179,17 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "reduceUserRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -208,7 +207,7 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { name: "reduceExternalIDMigrated", args: args{ event: getEvent(testEvent( - repository.EventType(user.UserIDPExternalIDMigratedType), + user.UserIDPExternalIDMigratedType, user.AggregateType, []byte(`{ "idpConfigId": "idp-config-id", @@ -219,9 +218,8 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { }, reduce: (&idpUserLinkProjection{}).reduceExternalIDMigrated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -243,19 +241,19 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "org IDPConfigRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(org.IDPConfigRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.IDPConfigRemovedEventType, + org.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), org.IDPConfigRemovedEventMapper), + ), org.IDPConfigRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceIDPConfigRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -273,19 +271,19 @@ func TestIDPUserLinkProjection_reduces(t *testing.T) { { name: "iam IDPConfigRemovedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.IDPConfigRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.IDPConfigRemovedEventType, + instance.AggregateType, + []byte(`{ "idpConfigId": "idp-config-id" }`), - ), instance.IDPConfigRemovedEventMapper), + ), instance.IDPConfigRemovedEventMapper), }, reduce: (&idpUserLinkProjection{}).reduceIDPConfigRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/instance.go b/internal/query/projection/instance.go index 43c0d84e1..68a110606 100644 --- a/internal/query/projection/instance.go +++ b/internal/query/projection/instance.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -25,39 +25,40 @@ const ( InstanceColumnDefaultLanguage = "default_language" ) -type instanceProjection struct { - crdb.StatementHandler +type instanceProjection struct{} + +func newInstanceProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(instanceProjection)) } -func newInstanceProjection(ctx context.Context, config crdb.StatementHandlerConfig) *instanceProjection { - p := new(instanceProjection) - config.ProjectionName = InstanceProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(InstanceColumnID, crdb.ColumnTypeText), - crdb.NewColumn(InstanceColumnName, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(InstanceColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(InstanceColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(InstanceColumnDefaultOrgID, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(InstanceColumnProjectID, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(InstanceColumnConsoleID, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(InstanceColumnConsoleAppID, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(InstanceColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(InstanceColumnDefaultLanguage, crdb.ColumnTypeText, crdb.Default("")), +func (*instanceProjection) Name() string { + return InstanceProjectionTable +} + +func (*instanceProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(InstanceColumnID, handler.ColumnTypeText), + handler.NewColumn(InstanceColumnName, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(InstanceColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(InstanceColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(InstanceColumnDefaultOrgID, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(InstanceColumnProjectID, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(InstanceColumnConsoleID, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(InstanceColumnConsoleAppID, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(InstanceColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(InstanceColumnDefaultLanguage, handler.ColumnTypeText, handler.Default("")), }, - crdb.NewPrimaryKey(InstanceColumnID), + handler.NewPrimaryKey(InstanceColumnID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *instanceProjection) reducers() []handler.AggregateReducer { +func (p *instanceProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceAddedEventType, Reduce: p.reduceInstanceAdded, @@ -96,7 +97,7 @@ func (p *instanceProjection) reduceInstanceAdded(event eventstore.Event) (*handl if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-29nlS", "reduce.wrong.event.type %s", instance.InstanceAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID), @@ -114,7 +115,7 @@ func reduceInstanceRemovedHelper(instanceIDCol string) func(event eventstore.Eve if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-22nlS", "reduce.wrong.event.type %s", instance.InstanceRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(instanceIDCol, e.Aggregate().ID), @@ -128,7 +129,7 @@ func (p *instanceProjection) reduceInstanceChanged(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-so2am1", "reduce.wrong.event.type %s", instance.InstanceChangedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnName, e.Name), @@ -146,7 +147,7 @@ func (p *instanceProjection) reduceDefaultOrgSet(event eventstore.Event) (*handl if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2n9f2", "reduce.wrong.event.type %s", instance.DefaultOrgSetEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnChangeDate, e.CreationDate()), @@ -164,7 +165,7 @@ func (p *instanceProjection) reduceIAMProjectSet(event eventstore.Event) (*handl if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-30o0e", "reduce.wrong.event.type %s", instance.ProjectSetEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnChangeDate, e.CreationDate()), @@ -182,7 +183,7 @@ func (p *instanceProjection) reduceConsoleSet(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dgf11", "reduce.wrong.event.type %s", instance.ConsoleSetEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnChangeDate, e.CreationDate()), @@ -201,7 +202,7 @@ func (p *instanceProjection) reduceDefaultLanguageSet(event eventstore.Event) (* if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-30o0e", "reduce.wrong.event.type %s", instance.DefaultLanguageSetEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnChangeDate, e.CreationDate()), diff --git a/internal/query/projection/instance_domain.go b/internal/query/projection/instance_domain.go index 18eded8cc..8eff389ce 100644 --- a/internal/query/projection/instance_domain.go +++ b/internal/query/projection/instance_domain.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -22,37 +22,42 @@ const ( InstanceDomainIsPrimaryCol = "is_primary" ) -type instanceDomainProjection struct { - crdb.StatementHandler +type instanceDomainProjection struct{} + +func newInstanceDomainProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(instanceDomainProjection)) } -func newInstanceDomainProjection(ctx context.Context, config crdb.StatementHandlerConfig) *instanceDomainProjection { - p := new(instanceDomainProjection) - config.ProjectionName = InstanceDomainTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(InstanceDomainInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(InstanceDomainCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(InstanceDomainChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(InstanceDomainSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(InstanceDomainDomainCol, crdb.ColumnTypeText), - crdb.NewColumn(InstanceDomainIsGeneratedCol, crdb.ColumnTypeBool), - crdb.NewColumn(InstanceDomainIsPrimaryCol, crdb.ColumnTypeBool), +func (*instanceDomainProjection) Name() string { + return InstanceDomainTable +} + +func (*instanceDomainProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(InstanceDomainInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(InstanceDomainCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(InstanceDomainChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(InstanceDomainSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(InstanceDomainDomainCol, handler.ColumnTypeText), + handler.NewColumn(InstanceDomainIsGeneratedCol, handler.ColumnTypeBool), + handler.NewColumn(InstanceDomainIsPrimaryCol, handler.ColumnTypeBool), }, - crdb.NewPrimaryKey(InstanceDomainInstanceIDCol, InstanceDomainDomainCol), - crdb.WithIndex(crdb.NewIndex("instance_domain", []string{InstanceDomainDomainCol})), + handler.NewPrimaryKey(InstanceDomainInstanceIDCol, InstanceDomainDomainCol), + handler.WithIndex( + handler.NewIndex("instance_domain", []string{InstanceDomainDomainCol}, + handler.WithInclude(InstanceDomainCreationDateCol, InstanceDomainChangeDateCol, InstanceDomainSequenceCol, InstanceDomainIsGeneratedCol, InstanceDomainIsPrimaryCol), + ), + ), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *instanceDomainProjection) reducers() []handler.AggregateReducer { +func (p *instanceDomainProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceDomainAddedEventType, Reduce: p.reduceDomainAdded, @@ -79,7 +84,7 @@ func (p *instanceDomainProjection) reduceDomainAdded(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-38nNf", "reduce.wrong.event.type %s", instance.InstanceDomainAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(InstanceDomainCreationDateCol, e.CreationDate()), @@ -98,9 +103,9 @@ func (p *instanceDomainProjection) reduceDomainPrimarySet(event eventstore.Event if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-f8nlw", "reduce.wrong.event.type %s", instance.InstanceDomainPrimarySetEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(InstanceDomainChangeDateCol, e.CreationDate()), handler.NewCol(InstanceDomainSequenceCol, e.Sequence()), @@ -111,7 +116,7 @@ func (p *instanceDomainProjection) reduceDomainPrimarySet(event eventstore.Event handler.NewCond(InstanceDomainIsPrimaryCol, true), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(InstanceDomainChangeDateCol, e.CreationDate()), handler.NewCol(InstanceDomainSequenceCol, e.Sequence()), @@ -130,7 +135,7 @@ func (p *instanceDomainProjection) reduceDomainRemoved(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-388Nk", "reduce.wrong.event.type %s", instance.InstanceDomainRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(InstanceDomainDomainCol, e.Domain), diff --git a/internal/query/projection/instance_domain_test.go b/internal/query/projection/instance_domain_test.go index 0106bbe85..7997bc0a8 100644 --- a/internal/query/projection/instance_domain_test.go +++ b/internal/query/projection/instance_domain_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -23,17 +22,17 @@ func TestInstanceDomainProjection_reduces(t *testing.T) { { name: "reduceDomainAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceDomainAddedEventType), - instance.AggregateType, - []byte(`{"domain": "domain.new", "generated": true}`), - ), instance.DomainAddedEventMapper), + event: getEvent( + testEvent( + instance.InstanceDomainAddedEventType, + instance.AggregateType, + []byte(`{"domain": "domain.new", "generated": true}`), + ), instance.DomainAddedEventMapper), }, reduce: (&instanceDomainProjection{}).reduceDomainAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -55,17 +54,17 @@ func TestInstanceDomainProjection_reduces(t *testing.T) { { name: "reduceDomainRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceDomainRemovedEventType), - instance.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), instance.DomainRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceDomainRemovedEventType, + instance.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), instance.DomainRemovedEventMapper), }, reduce: (&instanceDomainProjection{}).reduceDomainRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -82,17 +81,17 @@ func TestInstanceDomainProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/instance_member.go b/internal/query/projection/instance_member.go index 950327f2d..bf9daba6f 100644 --- a/internal/query/projection/instance_member.go +++ b/internal/query/projection/instance_member.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -19,31 +19,46 @@ const ( ) type instanceMemberProjection struct { - crdb.StatementHandler + es handler.EventStore } -func newInstanceMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *instanceMemberProjection { - p := new(instanceMemberProjection) - config.ProjectionName = InstanceMemberProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable( - append(memberColumns, crdb.NewColumn(InstanceColumnID, crdb.ColumnTypeText)), - crdb.NewPrimaryKey(MemberInstanceID, InstanceColumnID, MemberUserIDCol), - crdb.WithIndex(crdb.NewIndex("user_id", []string{MemberUserIDCol})), - crdb.WithIndex(crdb.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), +func newInstanceMemberProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, &instanceMemberProjection{es: config.Eventstore}) +} + +func (*instanceMemberProjection) Name() string { + return InstanceMemberProjectionTable +} + +func (*instanceMemberProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable( + append(memberColumns, handler.NewColumn(InstanceColumnID, handler.ColumnTypeText)), + handler.NewPrimaryKey(MemberInstanceID, InstanceColumnID, MemberUserIDCol), + handler.WithIndex(handler.NewIndex("user_id", []string{MemberUserIDCol})), + handler.WithIndex(handler.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), + handler.WithIndex( + handler.NewIndex("im_instance", []string{MemberInstanceID}, + handler.WithInclude( + MemberCreationDate, + MemberChangeDate, + MemberUserOwnerRemoved, + MemberRolesCol, + MemberSequence, + MemberResourceOwner, + MemberOwnerRemoved, + ), + ), + ), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *instanceMemberProjection) reducers() []handler.AggregateReducer { +func (p *instanceMemberProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.MemberAddedEventType, Reduce: p.reduceAdded, @@ -68,7 +83,7 @@ func (p *instanceMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceUserOwnerRemoved, @@ -77,7 +92,7 @@ func (p *instanceMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, @@ -93,7 +108,7 @@ func (p *instanceMemberProjection) reduceAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-pGNCu", "reduce.wrong.event.type %s", instance.MemberAddedEventType) } ctx := setMemberContext(e.Aggregate()) - userOwner, err := getResourceOwnerOfUser(ctx, p.Eventstore, e.Aggregate().InstanceID, e.UserID) + userOwner, err := getResourceOwnerOfUser(ctx, p.es, e.Aggregate().InstanceID, e.UserID) if err != nil { return nil, err } diff --git a/internal/query/projection/instance_member_test.go b/internal/query/projection/instance_member_test.go index 33c277f6c..bc1605aa1 100644 --- a/internal/query/projection/instance_member_test.go +++ b/internal/query/projection/instance_member_test.go @@ -10,8 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -30,34 +29,37 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "instance MemberAddedType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MemberAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MemberAddedEventType, + instance.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role"] }`), - ), instance.MemberAddedEventMapper), + ), instance.MemberAddedEventMapper), }, reduce: (&instanceMemberProjection{ - StatementHandler: getStatementHandlerWithFilters( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user-id", "org1").Aggregate, - "username1", - "firstname1", - "lastname1", - "nickname1", - "displayname1", - language.German, - domain.GenderMale, - "email1", - true, - ), - )(t)}).reduceAdded, + es: newMockEventStore().appendFilterResponse( + []eventstore.Event{ + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user-id", "org1").Aggregate, + "username1", + "firstname1", + "lastname1", + "nickname1", + "displayname1", + language.German, + domain.GenderMale, + "email1", + true, + ), + }, + ), + }).reduceAdded, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -66,7 +68,7 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { "user-id", "org1", false, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, anyArg{}, anyArg{}, uint64(15), @@ -83,26 +85,26 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "instance MemberChangedType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MemberChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MemberChangedEventType, + instance.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role", "changed"] }`), - ), instance.MemberChangedEventMapper), + ), instance.MemberChangedEventMapper), }, reduce: (&instanceMemberProjection{}).reduceChanged, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.instance_members3 SET (roles, change_date, sequence) = ($1, $2, $3) WHERE (instance_id = $4) AND (user_id = $5)", expectedArgs: []interface{}{ - database.StringArray{"role", "changed"}, + database.TextArray[string]{"role", "changed"}, anyArg{}, uint64(15), "instance-id", @@ -116,19 +118,19 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "instance MemberCascadeRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MemberCascadeRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MemberCascadeRemovedEventType, + instance.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), instance.MemberCascadeRemovedEventMapper), + ), instance.MemberCascadeRemovedEventMapper), }, reduce: (&instanceMemberProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -145,19 +147,19 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "instance MemberRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MemberRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MemberRemovedEventType, + instance.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), instance.MemberRemovedEventMapper), + ), instance.MemberRemovedEventMapper), }, reduce: (&instanceMemberProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: instance.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: instance.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -174,17 +176,17 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "user UserRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&instanceMemberProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -201,17 +203,17 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "org.OrgRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, reduce: (&instanceMemberProjection{}).reduceUserOwnerRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -228,17 +230,17 @@ func TestInstanceMemberProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/instance_test.go b/internal/query/projection/instance_test.go index 8287c3669..274e28f3f 100644 --- a/internal/query/projection/instance_test.go +++ b/internal/query/projection/instance_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -23,17 +22,17 @@ func TestInstanceProjection_reduces(t *testing.T) { { name: "reduceInstanceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceAddedEventType), - instance.AggregateType, - []byte(`{"name": "Name"}`), - ), instance.InstanceAddedEventMapper), + event: getEvent( + testEvent( + instance.InstanceAddedEventType, + instance.AggregateType, + []byte(`{"name": "Name"}`), + ), instance.InstanceAddedEventMapper), }, reduce: (&instanceProjection{}).reduceInstanceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -53,17 +52,17 @@ func TestInstanceProjection_reduces(t *testing.T) { { name: "reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(InstanceColumnID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -79,17 +78,17 @@ func TestInstanceProjection_reduces(t *testing.T) { { name: "reduceDefaultOrgSet", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DefaultOrgSetEventType), - instance.AggregateType, - []byte(`{"orgId": "orgid"}`), - ), instance.DefaultOrgSetMapper), + event: getEvent( + testEvent( + instance.DefaultOrgSetEventType, + instance.AggregateType, + []byte(`{"orgId": "orgid"}`), + ), instance.DefaultOrgSetMapper), }, reduce: (&instanceProjection{}).reduceDefaultOrgSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -108,17 +107,17 @@ func TestInstanceProjection_reduces(t *testing.T) { { name: "reduceProjectIDSet", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.ProjectSetEventType), - instance.AggregateType, - []byte(`{"iamProjectId": "project-id"}`), - ), instance.ProjectSetMapper), + event: getEvent( + testEvent( + instance.ProjectSetEventType, + instance.AggregateType, + []byte(`{"iamProjectId": "project-id"}`), + ), instance.ProjectSetMapper), }, reduce: (&instanceProjection{}).reduceIAMProjectSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -137,17 +136,17 @@ func TestInstanceProjection_reduces(t *testing.T) { { name: "reduceDefaultLanguageSet", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DefaultLanguageSetEventType), - instance.AggregateType, - []byte(`{"language": "en"}`), - ), instance.DefaultLanguageSetMapper), + event: getEvent( + testEvent( + instance.DefaultLanguageSetEventType, + instance.AggregateType, + []byte(`{"language": "en"}`), + ), instance.DefaultLanguageSetMapper), }, reduce: (&instanceProjection{}).reduceDefaultLanguageSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/key.go b/internal/query/projection/key.go index fabc76812..d15cb62ea 100644 --- a/internal/query/projection/key.go +++ b/internal/query/projection/key.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/keypair" ) @@ -48,71 +48,74 @@ const ( ) type keyProjection struct { - crdb.StatementHandler encryptionAlgorithm crypto.EncryptionAlgorithm certEncryptionAlgorithm crypto.EncryptionAlgorithm } -func newKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, certEncryptionAlgorithm crypto.EncryptionAlgorithm) *keyProjection { - p := new(keyProjection) - config.ProjectionName = KeyProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(KeyColumnID, crdb.ColumnTypeText), - crdb.NewColumn(KeyColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(KeyColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(KeyColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(KeyColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(KeyColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(KeyColumnAlgorithm, crdb.ColumnTypeText, crdb.Default("")), - crdb.NewColumn(KeyColumnUse, crdb.ColumnTypeEnum, crdb.Default(0)), - }, - crdb.NewPrimaryKey(KeyColumnInstanceID, KeyColumnID), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(KeyPrivateColumnID, crdb.ColumnTypeText), - crdb.NewColumn(KeyPrivateColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(KeyPrivateColumnExpiry, crdb.ColumnTypeTimestamp), - crdb.NewColumn(KeyPrivateColumnKey, crdb.ColumnTypeJSONB), - }, - crdb.NewPrimaryKey(KeyPrivateColumnInstanceID, KeyPrivateColumnID), - privateKeyTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(KeyPublicColumnID, crdb.ColumnTypeText), - crdb.NewColumn(KeyPublicColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(KeyPublicColumnExpiry, crdb.ColumnTypeTimestamp), - crdb.NewColumn(KeyPublicColumnKey, crdb.ColumnTypeBytes), - }, - crdb.NewPrimaryKey(KeyPublicColumnInstanceID, KeyPublicColumnID), - publicKeyTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(CertificateColumnID, crdb.ColumnTypeText), - crdb.NewColumn(CertificateColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(CertificateColumnExpiry, crdb.ColumnTypeTimestamp), - crdb.NewColumn(CertificateColumnCertificate, crdb.ColumnTypeBytes), - }, - crdb.NewPrimaryKey(CertificateColumnInstanceID, CertificateColumnID), - certificateTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - ) - p.encryptionAlgorithm = keyEncryptionAlgorithm - p.certEncryptionAlgorithm = certEncryptionAlgorithm - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - - return p +func newKeyProjection(ctx context.Context, config handler.Config, keyEncryptionAlgorithm, certEncryptionAlgorithm crypto.EncryptionAlgorithm) *handler.Handler { + p := &keyProjection{ + encryptionAlgorithm: keyEncryptionAlgorithm, + certEncryptionAlgorithm: certEncryptionAlgorithm, + } + return handler.NewHandler(ctx, &config, p) } -func (p *keyProjection) reducers() []handler.AggregateReducer { +func (*keyProjection) Name() string { + return KeyProjectionTable +} + +func (*keyProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(KeyColumnID, handler.ColumnTypeText), + handler.NewColumn(KeyColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(KeyColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(KeyColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(KeyColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(KeyColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(KeyColumnAlgorithm, handler.ColumnTypeText, handler.Default("")), + handler.NewColumn(KeyColumnUse, handler.ColumnTypeEnum, handler.Default(0)), + }, + handler.NewPrimaryKey(KeyColumnInstanceID, KeyColumnID), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(KeyPrivateColumnID, handler.ColumnTypeText), + handler.NewColumn(KeyPrivateColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(KeyPrivateColumnExpiry, handler.ColumnTypeTimestamp), + handler.NewColumn(KeyPrivateColumnKey, handler.ColumnTypeJSONB), + }, + handler.NewPrimaryKey(KeyPrivateColumnInstanceID, KeyPrivateColumnID), + privateKeyTableSuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(KeyPublicColumnID, handler.ColumnTypeText), + handler.NewColumn(KeyPublicColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(KeyPublicColumnExpiry, handler.ColumnTypeTimestamp), + handler.NewColumn(KeyPublicColumnKey, handler.ColumnTypeBytes), + }, + handler.NewPrimaryKey(KeyPublicColumnInstanceID, KeyPublicColumnID), + publicKeyTableSuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(CertificateColumnID, handler.ColumnTypeText), + handler.NewColumn(CertificateColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(CertificateColumnExpiry, handler.ColumnTypeTimestamp), + handler.NewColumn(CertificateColumnCertificate, handler.ColumnTypeBytes), + }, + handler.NewPrimaryKey(CertificateColumnInstanceID, CertificateColumnID), + certificateTableSuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + ) +} + +func (p *keyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: keypair.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: keypair.AddedEventType, Reduce: p.reduceKeyPairAdded, @@ -125,7 +128,7 @@ func (p *keyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(KeyColumnInstanceID), @@ -141,10 +144,10 @@ func (p *keyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Sta return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAbr2", "reduce.wrong.event.type %s", keypair.AddedEventType) } if e.PrivateKey.Expiry.Before(time.Now()) && e.PublicKey.Expiry.Before(time.Now()) { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - creates := []func(eventstore.Event) crdb.Exec{ - crdb.AddCreateStatement( + creates := []func(eventstore.Event) handler.Exec{ + handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyColumnID, e.Aggregate().ID), handler.NewCol(KeyColumnCreationDate, e.CreationDate()), @@ -158,14 +161,14 @@ func (p *keyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Sta ), } if e.PrivateKey.Expiry.After(time.Now()) { - creates = append(creates, crdb.AddCreateStatement( + creates = append(creates, handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPrivateColumnID, e.Aggregate().ID), handler.NewCol(KeyPrivateColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPrivateColumnExpiry, e.PrivateKey.Expiry), handler.NewCol(KeyPrivateColumnKey, e.PrivateKey.Key), }, - crdb.WithTableSuffix(privateKeyTableSuffix), + handler.WithTableSuffix(privateKeyTableSuffix), )) } if e.PublicKey.Expiry.After(time.Now()) { @@ -173,18 +176,18 @@ func (p *keyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Sta if err != nil { return nil, errors.ThrowInternal(err, "HANDL-DAg2f", "cannot decrypt public key") } - creates = append(creates, crdb.AddCreateStatement( + creates = append(creates, handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPublicColumnID, e.Aggregate().ID), handler.NewCol(KeyPublicColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPublicColumnExpiry, e.PublicKey.Expiry), handler.NewCol(KeyPublicColumnKey, publicKey), }, - crdb.WithTableSuffix(publicKeyTableSuffix), + handler.WithTableSuffix(publicKeyTableSuffix), )) } - return crdb.NewMultiStatement(e, creates...), nil + return handler.NewMultiStatement(e, creates...), nil } func (p *keyProjection) reduceCertificateAdded(event eventstore.Event) (*handler.Statement, error) { @@ -194,7 +197,7 @@ func (p *keyProjection) reduceCertificateAdded(event eventstore.Event) (*handler } if e.Certificate.Expiry.Before(time.Now()) { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } certificate, err := crypto.Decrypt(e.Certificate.Key, p.certEncryptionAlgorithm) @@ -202,15 +205,15 @@ func (p *keyProjection) reduceCertificateAdded(event eventstore.Event) (*handler return nil, errors.ThrowInternal(err, "HANDL-Dajwig2f", "cannot decrypt certificate") } - creates := []func(eventstore.Event) crdb.Exec{crdb.AddCreateStatement( + creates := []func(eventstore.Event) handler.Exec{handler.AddCreateStatement( []handler.Column{ handler.NewCol(CertificateColumnID, e.Aggregate().ID), handler.NewCol(CertificateColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(CertificateColumnExpiry, e.Certificate.Expiry), handler.NewCol(CertificateColumnCertificate, certificate), }, - crdb.WithTableSuffix(certificateTableSuffix), + handler.WithTableSuffix(certificateTableSuffix), )} - return crdb.NewMultiStatement(e, creates...), nil + return handler.NewMultiStatement(e, creates...), nil } diff --git a/internal/query/projection/key_test.go b/internal/query/projection/key_test.go index 2c93e3859..d6482ec7f 100644 --- a/internal/query/projection/key_test.go +++ b/internal/query/projection/key_test.go @@ -11,8 +11,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/keypair" ) @@ -30,17 +29,17 @@ func TestKeyProjection_reduces(t *testing.T) { { name: "reduceKeyPairAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(keypair.AddedEventType), - keypair.AggregateType, - keypairAddedEventData(domain.KeyUsageSigning, time.Now().Add(time.Hour)), - ), keypair.AddedEventMapper), + event: getEvent( + testEvent( + keypair.AddedEventType, + keypair.AggregateType, + keypairAddedEventData(domain.KeyUsageSigning, time.Now().Add(time.Hour)), + ), keypair.AddedEventMapper), }, reduce: (&keyProjection{encryptionAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t))}).reduceKeyPairAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("key_pair"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("key_pair"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -86,34 +85,34 @@ func TestKeyProjection_reduces(t *testing.T) { { name: "reduceKeyPairAdded expired", args: args{ - event: getEvent(testEvent( - repository.EventType(keypair.AddedEventType), - keypair.AggregateType, - keypairAddedEventData(domain.KeyUsageSigning, time.Now().Add(-time.Hour)), - ), keypair.AddedEventMapper), + event: getEvent( + testEvent( + keypair.AddedEventType, + keypair.AggregateType, + keypairAddedEventData(domain.KeyUsageSigning, time.Now().Add(-time.Hour)), + ), keypair.AddedEventMapper), }, reduce: (&keyProjection{}).reduceKeyPairAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("key_pair"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("key_pair"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(KeyColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -129,17 +128,17 @@ func TestKeyProjection_reduces(t *testing.T) { { name: "reduceCertificateAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(keypair.AddedCertificateEventType), - keypair.AggregateType, - certificateAddedEventData(domain.KeyUsageSAMLMetadataSigning, time.Now().Add(time.Hour)), - ), keypair.AddedCertificateEventMapper), + event: getEvent( + testEvent( + keypair.AddedCertificateEventType, + keypair.AggregateType, + certificateAddedEventData(domain.KeyUsageSAMLMetadataSigning, time.Now().Add(time.Hour)), + ), keypair.AddedCertificateEventMapper), }, reduce: (&keyProjection{certEncryptionAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t))}).reduceCertificateAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("key_pair"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("key_pair"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/label_policy.go b/internal/query/projection/label_policy.go index fb3cd221a..f8f6b03d5 100644 --- a/internal/query/projection/label_policy.go +++ b/internal/query/projection/label_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -45,55 +45,56 @@ const ( LabelPolicyDarkIconURLCol = "dark_icon_url" ) -type labelPolicyProjection struct { - crdb.StatementHandler +type labelPolicyProjection struct{} + +func newLabelPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(labelPolicyProjection)) } -func newLabelPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *labelPolicyProjection { - p := new(labelPolicyProjection) - config.ProjectionName = LabelPolicyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(LabelPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LabelPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LabelPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LabelPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LabelPolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(LabelPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LabelPolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(LabelPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LabelPolicyHideLoginNameSuffixCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LabelPolicyWatermarkDisabledCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LabelPolicyShouldErrorPopupCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LabelPolicyFontURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightPrimaryColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightWarnColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightBackgroundColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightFontColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightLogoURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyLightIconURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkPrimaryColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkWarnColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkBackgroundColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkFontColorCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkLogoURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyDarkIconURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(LabelPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*labelPolicyProjection) Name() string { + return LabelPolicyTable +} + +func (*labelPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(LabelPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(LabelPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LabelPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LabelPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(LabelPolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(LabelPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LabelPolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(LabelPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LabelPolicyHideLoginNameSuffixCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LabelPolicyWatermarkDisabledCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LabelPolicyShouldErrorPopupCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LabelPolicyFontURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightPrimaryColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightWarnColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightBackgroundColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightFontColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightLogoURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyLightIconURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkPrimaryColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkWarnColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkBackgroundColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkFontColorCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkLogoURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyDarkIconURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(LabelPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(LabelPolicyInstanceIDCol, LabelPolicyIDCol, LabelPolicyStateCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LabelPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(LabelPolicyInstanceIDCol, LabelPolicyIDCol, LabelPolicyStateCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LabelPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *labelPolicyProjection) reducers() []handler.AggregateReducer { +func (p *labelPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.LabelPolicyAddedEventType, Reduce: p.reduceAdded, @@ -162,7 +163,7 @@ func (p *labelPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.LabelPolicyAddedEventType, Reduce: p.reduceAdded, @@ -241,11 +242,11 @@ func (p *labelPolicyProjection) reduceAdded(event eventstore.Event) (*handler.St default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-CSE7A", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyAddedEventType, instance.LabelPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ - handler.NewCol(LabelPolicyCreationDateCol, policyEvent.CreationDate()), - handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreationDate()), + handler.NewCol(LabelPolicyCreationDateCol, policyEvent.CreatedAt()), + handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, policyEvent.Sequence()), handler.NewCol(LabelPolicyIDCol, policyEvent.Aggregate().ID), handler.NewCol(LabelPolicyStateCol, domain.LabelPolicyStatePreview), @@ -277,7 +278,7 @@ func (p *labelPolicyProjection) reduceChanged(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-qgVug", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyChangedEventType, instance.LabelPolicyChangedEventType}) } cols := []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, policyEvent.Sequence()), } if policyEvent.PrimaryColor != nil { @@ -313,7 +314,7 @@ func (p *labelPolicyProjection) reduceChanged(event eventstore.Event) (*handler. if policyEvent.DisableWatermark != nil { cols = append(cols, handler.NewCol(LabelPolicyWatermarkDisabledCol, *policyEvent.DisableWatermark)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -328,7 +329,7 @@ func (p *labelPolicyProjection) reduceRemoved(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-ATMBz", "reduce.wrong.event.type %s", org.LabelPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(LabelPolicyIDCol, policyEvent.Aggregate().ID), @@ -343,7 +344,7 @@ func (p *labelPolicyProjection) reduceActivated(event eventstore.Event) (*handle default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-dldEU", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyActivatedEventType, instance.LabelPolicyActivatedEventType}) } - return crdb.NewCopyStatement( + return handler.NewCopyStatement( event, []handler.Column{ handler.NewCol(LabelPolicyInstanceIDCol, nil), @@ -351,7 +352,7 @@ func (p *labelPolicyProjection) reduceActivated(event eventstore.Event) (*handle handler.NewCol(LabelPolicyStateCol, nil), }, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), handler.NewCol(LabelPolicyStateCol, domain.LabelPolicyStateActive), handler.NewCol(LabelPolicyCreationDateCol, nil), @@ -424,10 +425,10 @@ func (p *labelPolicyProjection) reduceLogoAdded(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-4wbOI", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyLogoAddedEventType, instance.LabelPolicyLogoAddedEventType, org.LabelPolicyLogoDarkAddedEventType, instance.LabelPolicyLogoDarkAddedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), storeKey, }, @@ -453,10 +454,10 @@ func (p *labelPolicyProjection) reduceLogoRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-kg8H4", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyLogoRemovedEventType, instance.LabelPolicyLogoRemovedEventType, org.LabelPolicyLogoDarkRemovedEventType, instance.LabelPolicyLogoDarkRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), handler.NewCol(col, nil), }, @@ -482,10 +483,10 @@ func (p *labelPolicyProjection) reduceIconAdded(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-e2JFz", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyIconAddedEventType, instance.LabelPolicyIconAddedEventType, org.LabelPolicyIconDarkAddedEventType, instance.LabelPolicyIconDarkAddedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), storeKey, }, @@ -511,10 +512,10 @@ func (p *labelPolicyProjection) reduceIconRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-gfgbY", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyIconRemovedEventType, instance.LabelPolicyIconRemovedEventType, org.LabelPolicyIconDarkRemovedEventType, instance.LabelPolicyIconDarkRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), handler.NewCol(col, nil), }, @@ -536,10 +537,10 @@ func (p *labelPolicyProjection) reduceFontAdded(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-65i9W", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyFontAddedEventType, instance.LabelPolicyFontAddedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), storeKey, }, @@ -561,10 +562,10 @@ func (p *labelPolicyProjection) reduceFontRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-xf32J", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyFontRemovedEventType, instance.LabelPolicyFontRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), handler.NewCol(col, nil), }, @@ -583,10 +584,10 @@ func (p *labelPolicyProjection) reduceAssetsRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-qi39A", "reduce.wrong.event.type %v", []eventstore.EventType{org.LabelPolicyAssetsRemovedEventType, instance.LabelPolicyAssetsRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, event.CreatedAt()), handler.NewCol(LabelPolicySequenceCol, event.Sequence()), handler.NewCol(LabelPolicyLightLogoURLCol, nil), handler.NewCol(LabelPolicyLightIconURLCol, nil), @@ -607,7 +608,7 @@ func (p *labelPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Su6pX", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(LabelPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/label_policy_test.go b/internal/query/projection/label_policy_test.go index c046bb01d..553b67fc3 100644 --- a/internal/query/projection/label_policy_test.go +++ b/internal/query/projection/label_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,17 +24,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyAddedEventType), - org.AggregateType, - []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), - ), org.LabelPolicyAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyAddedEventType, + org.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), org.LabelPolicyAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -69,17 +68,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyChangedEventType), - org.AggregateType, - []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), - ), org.LabelPolicyChangedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyChangedEventType, + org.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), org.LabelPolicyChangedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -103,17 +102,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.LabelPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.LabelPolicyRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -130,17 +129,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(LabelPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -156,17 +155,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceActivated", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyActivatedEventType), - org.AggregateType, - nil, - ), org.LabelPolicyActivatedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyActivatedEventType, + org.AggregateType, + nil, + ), org.LabelPolicyActivatedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceActivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -187,17 +186,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceLogoAdded light", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyLogoAddedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), org.LabelPolicyLogoAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyLogoAddedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -218,17 +217,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceLogoAdded dark", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyLogoDarkAddedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), org.LabelPolicyLogoDarkAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyLogoDarkAddedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoDarkAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -249,17 +248,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceIconAdded light", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyIconAddedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), org.LabelPolicyIconAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyIconAddedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -280,17 +279,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceIconAdded dark", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyIconDarkAddedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), org.LabelPolicyIconDarkAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyIconDarkAddedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconDarkAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -311,17 +310,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceLogoRemoved light", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyLogoRemovedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), org.LabelPolicyLogoRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyLogoRemovedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -342,17 +341,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceLogoRemoved dark", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyLogoDarkRemovedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), org.LabelPolicyLogoDarkRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyLogoDarkRemovedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoDarkRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -373,17 +372,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceIconRemoved light", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyIconRemovedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), org.LabelPolicyIconRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyIconRemovedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -404,17 +403,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceIconRemoved dark", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyIconDarkRemovedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), org.LabelPolicyIconDarkRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyIconDarkRemovedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconDarkRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -435,17 +434,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceFontAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyFontAddedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/font.ttf"}`), - ), org.LabelPolicyFontAddedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyFontAddedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), org.LabelPolicyFontAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceFontAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -466,17 +465,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceFontRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyFontRemovedEventType), - org.AggregateType, - []byte(`{"storeKey": "/path/to/font.ttf"}`), - ), org.LabelPolicyFontRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyFontRemovedEventType, + org.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), org.LabelPolicyFontRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceFontRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -497,17 +496,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "org reduceAssetsRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LabelPolicyAssetsRemovedEventType), - org.AggregateType, - nil, - ), org.LabelPolicyAssetsRemovedEventMapper), + event: getEvent( + testEvent( + org.LabelPolicyAssetsRemovedEventType, + org.AggregateType, + nil, + ), org.LabelPolicyAssetsRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceAssetsRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -532,17 +531,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyAddedEventType), - instance.AggregateType, - []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), - ), instance.LabelPolicyAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyAddedEventType, + instance.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), instance.LabelPolicyAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -576,17 +575,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyChangedEventType), - instance.AggregateType, - []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b", "primaryColorDark": "#ffffff","backgroundColorDark": "#ffffff", "warnColorDark": "#ffffff", "fontColorDark": "#ffffff", "hideLoginNameSuffix": true, "errorMsgPopup": true, "disableWatermark": true}`), - ), instance.LabelPolicyChangedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyChangedEventType, + instance.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b", "primaryColorDark": "#ffffff","backgroundColorDark": "#ffffff", "warnColorDark": "#ffffff", "fontColorDark": "#ffffff", "hideLoginNameSuffix": true, "errorMsgPopup": true, "disableWatermark": true}`), + ), instance.LabelPolicyChangedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -617,17 +616,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceActivated", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyActivatedEventType), - instance.AggregateType, - nil, - ), instance.LabelPolicyActivatedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyActivatedEventType, + instance.AggregateType, + nil, + ), instance.LabelPolicyActivatedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceActivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -648,17 +647,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceLogoAdded light", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyLogoAddedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), instance.LabelPolicyLogoAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyLogoAddedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), instance.LabelPolicyLogoAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -679,17 +678,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceLogoAdded dark", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyLogoDarkAddedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), instance.LabelPolicyLogoDarkAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyLogoDarkAddedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), instance.LabelPolicyLogoDarkAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -710,17 +709,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceIconAdded light", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyIconAddedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), instance.LabelPolicyIconAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyIconAddedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), instance.LabelPolicyIconAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -741,17 +740,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceIconAdded dark", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyIconDarkAddedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), instance.LabelPolicyIconDarkAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyIconDarkAddedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), instance.LabelPolicyIconDarkAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -772,17 +771,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceLogoRemoved light", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyLogoRemovedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), instance.LabelPolicyLogoRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyLogoRemovedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), instance.LabelPolicyLogoRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -803,17 +802,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceLogoRemoved dark", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyLogoDarkRemovedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/logo.png"}`), - ), instance.LabelPolicyLogoDarkRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyLogoDarkRemovedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), instance.LabelPolicyLogoDarkRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceLogoRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -834,17 +833,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceIconRemoved light", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyIconRemovedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), instance.LabelPolicyIconRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyIconRemovedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), instance.LabelPolicyIconRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -865,17 +864,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceIconRemoved dark", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyIconDarkRemovedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/icon.png"}`), - ), instance.LabelPolicyIconDarkRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyIconDarkRemovedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), instance.LabelPolicyIconDarkRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceIconRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -896,17 +895,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceFontAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyFontAddedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/font.ttf"}`), - ), instance.LabelPolicyFontAddedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyFontAddedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), instance.LabelPolicyFontAddedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceFontAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -927,17 +926,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceFontRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyFontRemovedEventType), - instance.AggregateType, - []byte(`{"storeKey": "/path/to/font.ttf"}`), - ), instance.LabelPolicyFontRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyFontRemovedEventType, + instance.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), instance.LabelPolicyFontRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceFontRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -958,17 +957,17 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { { name: "instance reduceAssetsRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LabelPolicyAssetsRemovedEventType), - instance.AggregateType, - nil, - ), instance.LabelPolicyAssetsRemovedEventMapper), + event: getEvent( + testEvent( + instance.LabelPolicyAssetsRemovedEventType, + instance.AggregateType, + nil, + ), instance.LabelPolicyAssetsRemovedEventMapper), }, reduce: (&labelPolicyProjection{}).reduceAssetsRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -994,16 +993,16 @@ func TestLabelPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&labelPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/lockout_policy.go b/internal/query/projection/lockout_policy.go index 1a6f9f7a7..9ab9f6f51 100644 --- a/internal/query/projection/lockout_policy.go +++ b/internal/query/projection/lockout_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -29,41 +29,42 @@ const ( LockoutPolicyOwnerRemovedCol = "owner_removed" ) -type lockoutPolicyProjection struct { - crdb.StatementHandler +type lockoutPolicyProjection struct{} + +func newLockoutPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(lockoutPolicyProjection)) } -func newLockoutPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *lockoutPolicyProjection { - p := new(lockoutPolicyProjection) - config.ProjectionName = LockoutPolicyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(LockoutPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LockoutPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LockoutPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LockoutPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LockoutPolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(LockoutPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LockoutPolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(LockoutPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LockoutPolicyMaxPasswordAttemptsCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LockoutPolicyShowLockOutFailuresCol, crdb.ColumnTypeBool), - crdb.NewColumn(LockoutPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*lockoutPolicyProjection) Name() string { + return LockoutPolicyTable +} + +func (*lockoutPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(LockoutPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(LockoutPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LockoutPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LockoutPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(LockoutPolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(LockoutPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LockoutPolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(LockoutPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LockoutPolicyMaxPasswordAttemptsCol, handler.ColumnTypeInt64), + handler.NewColumn(LockoutPolicyShowLockOutFailuresCol, handler.ColumnTypeBool), + handler.NewColumn(LockoutPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(LockoutPolicyInstanceIDCol, LockoutPolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LockoutPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(LockoutPolicyInstanceIDCol, LockoutPolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LockoutPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *lockoutPolicyProjection) reducers() []handler.AggregateReducer { +func (p *lockoutPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.LockoutPolicyAddedEventType, Reduce: p.reduceAdded, @@ -84,7 +85,7 @@ func (p *lockoutPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.LockoutPolicyAddedEventType, Reduce: p.reduceAdded, @@ -115,7 +116,7 @@ func (p *lockoutPolicyProjection) reduceAdded(event eventstore.Event) (*handler. default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-d8mZO", "reduce.wrong.event.type, %v", []eventstore.EventType{org.LockoutPolicyAddedEventType, instance.LockoutPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(LockoutPolicyCreationDateCol, policyEvent.CreationDate()), @@ -151,7 +152,7 @@ func (p *lockoutPolicyProjection) reduceChanged(event eventstore.Event) (*handle if policyEvent.ShowLockOutFailures != nil { cols = append(cols, handler.NewCol(LockoutPolicyShowLockOutFailuresCol, *policyEvent.ShowLockOutFailures)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -165,7 +166,7 @@ func (p *lockoutPolicyProjection) reduceRemoved(event eventstore.Event) (*handle if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Bqut9", "reduce.wrong.event.type %s", org.LockoutPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(LockoutPolicyIDCol, policyEvent.Aggregate().ID), @@ -179,7 +180,7 @@ func (p *lockoutPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-IoW0x", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(LockoutPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/lockout_policy_test.go b/internal/query/projection/lockout_policy_test.go index 338f3a92a..65e712574 100644 --- a/internal/query/projection/lockout_policy_test.go +++ b/internal/query/projection/lockout_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,20 +24,20 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LockoutPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LockoutPolicyAddedEventType, + org.AggregateType, + []byte(`{ "maxPasswordAttempts": 10, "showLockOutFailures": true }`), - ), org.LockoutPolicyAddedEventMapper), + ), org.LockoutPolicyAddedEventMapper), }, reduce: (&lockoutPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -64,19 +63,19 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (&lockoutPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LockoutPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LockoutPolicyChangedEventType, + org.AggregateType, + []byte(`{ "maxPasswordAttempts": 10, "showLockOutFailures": true }`), - ), org.LockoutPolicyChangedEventMapper), + ), org.LockoutPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -98,16 +97,16 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&lockoutPolicyProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LockoutPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.LockoutPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.LockoutPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.LockoutPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -124,17 +123,17 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(LockoutPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -151,19 +150,19 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&lockoutPolicyProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LockoutPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LockoutPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "maxPasswordAttempts": 10, "showLockOutFailures": true }`), - ), instance.LockoutPolicyAddedEventMapper), + ), instance.LockoutPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -189,19 +188,19 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (&lockoutPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LockoutPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LockoutPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "maxPasswordAttempts": 10, "showLockOutFailures": true }`), - ), instance.LockoutPolicyChangedEventMapper), + ), instance.LockoutPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -223,16 +222,16 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&lockoutPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/login_name.go b/internal/query/projection/login_name.go index 2d21bdaff..d8383af0a 100644 --- a/internal/query/projection/login_name.go +++ b/internal/query/projection/login_name.go @@ -8,8 +8,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -171,61 +171,73 @@ func whenThenElse(when, then, el string) string { return "(CASE WHEN " + when + " THEN " + then + " ELSE " + el + " END)" } -type loginNameProjection struct { - crdb.StatementHandler +type loginNameProjection struct{} + +func newLoginNameProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(loginNameProjection)) } -func newLoginNameProjection(ctx context.Context, config crdb.StatementHandlerConfig) *loginNameProjection { - p := new(loginNameProjection) - config.ProjectionName = LoginNameProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewViewCheck( +func (*loginNameProjection) Name() string { + return LoginNameProjectionTable +} + +func (*loginNameProjection) Init() *old_handler.Check { + return handler.NewViewCheck( viewStmt, - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(LoginNameUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameUserUserNameCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameUserResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameUserInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameUserOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), - }, - crdb.NewPrimaryKey(LoginNameUserInstanceIDCol, LoginNameUserIDCol), + handler.NewSuffixedTable( + []*handler.InitColumn{ + handler.NewColumn(LoginNameUserIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameUserUserNameCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameUserResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameUserInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameUserOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), + }, + handler.NewPrimaryKey(LoginNameUserInstanceIDCol, LoginNameUserIDCol), loginNameUserSuffix, - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{LoginNameUserResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LoginNameUserOwnerRemovedCol})), + handler.WithIndex(handler.NewIndex("resource_owner", []string{LoginNameUserResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LoginNameUserOwnerRemovedCol})), + handler.WithIndex( + handler.NewIndex("lnu_instance_ro_id", []string{LoginNameUserInstanceIDCol, LoginNameUserResourceOwnerCol, LoginNameUserIDCol}, + handler.WithInclude( + LoginNameUserUserNameCol, + LoginNameUserOwnerRemovedCol, + ), + ), + ), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(LoginNameDomainNameCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameDomainIsPrimaryCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LoginNameDomainResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameDomainInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNameDomainOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), - }, - crdb.NewPrimaryKey(LoginNameDomainInstanceIDCol, LoginNameDomainResourceOwnerCol, LoginNameDomainNameCol), + handler.NewSuffixedTable( + []*handler.InitColumn{ + handler.NewColumn(LoginNameDomainNameCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameDomainIsPrimaryCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LoginNameDomainResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameDomainInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginNameDomainOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), + }, + handler.NewPrimaryKey(LoginNameDomainInstanceIDCol, LoginNameDomainResourceOwnerCol, LoginNameDomainNameCol), loginNameDomainSuffix, - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LoginNameDomainOwnerRemovedCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LoginNameDomainOwnerRemovedCol})), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(LoginNamePoliciesMustBeDomainCol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginNamePoliciesIsDefaultCol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginNamePoliciesResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNamePoliciesInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginNamePoliciesOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), - }, - crdb.NewPrimaryKey(LoginNamePoliciesInstanceIDCol, LoginNamePoliciesResourceOwnerCol), + handler.NewSuffixedTable( + []*handler.InitColumn{ + handler.NewColumn(LoginNamePoliciesMustBeDomainCol, handler.ColumnTypeBool), + handler.NewColumn(LoginNamePoliciesIsDefaultCol, handler.ColumnTypeBool), + handler.NewColumn(LoginNamePoliciesResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(LoginNamePoliciesInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginNamePoliciesOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), + }, + handler.NewPrimaryKey(LoginNamePoliciesInstanceIDCol, LoginNamePoliciesResourceOwnerCol), loginNamePolicySuffix, - crdb.WithIndex(crdb.NewIndex("is_default", []string{LoginNamePoliciesResourceOwnerCol, LoginNamePoliciesIsDefaultCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LoginNamePoliciesOwnerRemovedCol})), + handler.WithIndex(handler.NewIndex("is_default", []string{LoginNamePoliciesResourceOwnerCol, LoginNamePoliciesIsDefaultCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LoginNamePoliciesOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *loginNameProjection) reducers() []handler.AggregateReducer { +func (p *loginNameProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserV1AddedType, Reduce: p.reduceUserCreated, @@ -265,7 +277,7 @@ func (p *loginNameProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.DomainPolicyAddedEventType, Reduce: p.reduceOrgIAMPolicyAdded, @@ -298,7 +310,7 @@ func (p *loginNameProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.DomainPolicyAddedEventType, Reduce: p.reduceOrgIAMPolicyAdded, @@ -330,7 +342,7 @@ func (p *loginNameProjection) reduceUserCreated(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ayo69", "reduce.wrong.event.type %v", []eventstore.EventType{user.UserV1AddedType, user.HumanAddedType, user.UserV1RegisteredType, user.HumanRegisteredType, user.MachineAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( event, []handler.Column{ handler.NewCol(LoginNameUserIDCol, event.Aggregate().ID), @@ -338,7 +350,7 @@ func (p *loginNameProjection) reduceUserCreated(event eventstore.Event) (*handle handler.NewCol(LoginNameUserResourceOwnerCol, event.Aggregate().ResourceOwner), handler.NewCol(LoginNameUserInstanceIDCol, event.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), nil } @@ -348,13 +360,13 @@ func (p *loginNameProjection) reduceUserRemoved(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-QIe3C", "reduce.wrong.event.type %s", user.UserRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(LoginNameUserIDCol, e.Aggregate().ID), handler.NewCond(LoginNameUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), nil } @@ -364,7 +376,7 @@ func (p *loginNameProjection) reduceUserNameChanged(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-QlwjC", "reduce.wrong.event.type %s", user.UserUserNameChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ handler.NewCol(LoginNameUserUserNameCol, e.UserName), @@ -373,7 +385,7 @@ func (p *loginNameProjection) reduceUserNameChanged(event eventstore.Event) (*ha handler.NewCond(LoginNameUserIDCol, e.Aggregate().ID), handler.NewCond(LoginNameUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), nil } @@ -383,7 +395,7 @@ func (p *loginNameProjection) reduceUserDomainClaimed(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-AQMBY", "reduce.wrong.event.type %s", user.UserDomainClaimedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ handler.NewCol(LoginNameUserUserNameCol, e.UserName), @@ -392,7 +404,7 @@ func (p *loginNameProjection) reduceUserDomainClaimed(event eventstore.Event) (* handler.NewCond(LoginNameUserIDCol, e.Aggregate().ID), handler.NewCond(LoginNameUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), nil } @@ -413,7 +425,7 @@ func (p *loginNameProjection) reduceOrgIAMPolicyAdded(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-yCV6S", "reduce.wrong.event.type %v", []eventstore.EventType{org.DomainPolicyAddedEventType, instance.DomainPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( event, []handler.Column{ handler.NewCol(LoginNamePoliciesMustBeDomainCol, policyEvent.UserLoginMustBeDomain), @@ -421,7 +433,7 @@ func (p *loginNameProjection) reduceOrgIAMPolicyAdded(event eventstore.Event) (* handler.NewCol(LoginNamePoliciesResourceOwnerCol, policyEvent.Aggregate().ResourceOwner), handler.NewCol(LoginNamePoliciesInstanceIDCol, policyEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNamePolicySuffix), + handler.WithTableSuffix(loginNamePolicySuffix), ), nil } @@ -438,10 +450,10 @@ func (p *loginNameProjection) reduceDomainPolicyChanged(event eventstore.Event) } if policyEvent.UserLoginMustBeDomain == nil { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ handler.NewCol(LoginNamePoliciesMustBeDomainCol, *policyEvent.UserLoginMustBeDomain), @@ -450,7 +462,7 @@ func (p *loginNameProjection) reduceDomainPolicyChanged(event eventstore.Event) handler.NewCond(LoginNamePoliciesResourceOwnerCol, policyEvent.Aggregate().ResourceOwner), handler.NewCond(LoginNamePoliciesInstanceIDCol, policyEvent.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNamePolicySuffix), + handler.WithTableSuffix(loginNamePolicySuffix), ), nil } @@ -460,13 +472,13 @@ func (p *loginNameProjection) reduceDomainPolicyRemoved(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ysEeB", "reduce.wrong.event.type %s", org.DomainPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(LoginNamePoliciesResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCond(LoginNamePoliciesInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNamePolicySuffix), + handler.WithTableSuffix(loginNamePolicySuffix), ), nil } @@ -476,14 +488,14 @@ func (p *loginNameProjection) reduceDomainVerified(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-weGAh", "reduce.wrong.event.type %s", org.OrgDomainVerifiedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( event, []handler.Column{ handler.NewCol(LoginNameDomainNameCol, e.Domain), handler.NewCol(LoginNameDomainResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCol(LoginNameDomainInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), nil } @@ -493,9 +505,9 @@ func (p *loginNameProjection) reducePrimaryDomainSet(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-eOXPN", "reduce.wrong.event.type %s", org.OrgDomainPrimarySetEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(LoginNameDomainIsPrimaryCol, false), }, @@ -504,9 +516,9 @@ func (p *loginNameProjection) reducePrimaryDomainSet(event eventstore.Event) (*h handler.NewCond(LoginNameDomainIsPrimaryCol, true), handler.NewCond(LoginNameDomainInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(LoginNameDomainIsPrimaryCol, true), }, @@ -515,7 +527,7 @@ func (p *loginNameProjection) reducePrimaryDomainSet(event eventstore.Event) (*h handler.NewCond(LoginNameDomainResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCond(LoginNameDomainInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), ), nil } @@ -526,14 +538,14 @@ func (p *loginNameProjection) reduceDomainRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-4RHYq", "reduce.wrong.event.type %s", org.OrgDomainRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(LoginNameDomainNameCol, e.Domain), handler.NewCond(LoginNameDomainResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCond(LoginNameDomainInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), nil } @@ -543,25 +555,25 @@ func (p *loginNameProjection) reduceInstanceRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASeg3", "reduce.wrong.event.type %s", instance.InstanceRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( event, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNameDomainInstanceIDCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNamePoliciesInstanceIDCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNamePolicySuffix), + handler.WithTableSuffix(loginNamePolicySuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNameUserInstanceIDCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), ), nil } @@ -572,28 +584,28 @@ func (p *loginNameProjection) reduceOwnerRemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-px02mo", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( event, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNameDomainInstanceIDCol, e.Aggregate().InstanceID), handler.NewCond(LoginNameDomainResourceOwnerCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNameDomainSuffix), + handler.WithTableSuffix(loginNameDomainSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNamePoliciesInstanceIDCol, e.Aggregate().InstanceID), handler.NewCond(LoginNamePoliciesResourceOwnerCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNamePolicySuffix), + handler.WithTableSuffix(loginNamePolicySuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(LoginNameUserInstanceIDCol, e.Aggregate().InstanceID), handler.NewCond(LoginNameUserResourceOwnerCol, e.Aggregate().ID), }, - crdb.WithTableSuffix(loginNameUserSuffix), + handler.WithTableSuffix(loginNameUserSuffix), ), ), nil } diff --git a/internal/query/projection/login_name_test.go b/internal/query/projection/login_name_test.go index 92f1b583d..36c38ec76 100644 --- a/internal/query/projection/login_name_test.go +++ b/internal/query/projection/login_name_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -25,19 +24,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user HumanAddedType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanAddedType, + user.AggregateType, + []byte(`{ "userName": "human-added" }`), - ), user.HumanAddedEventMapper), + ), user.HumanAddedEventMapper), }, reduce: (&loginNameProjection{}).reduceUserCreated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -56,19 +55,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user HumanRegisteredType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanRegisteredType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanRegisteredType, + user.AggregateType, + []byte(`{ "userName": "human-registered" }`), - ), user.HumanRegisteredEventMapper), + ), user.HumanRegisteredEventMapper), }, reduce: (&loginNameProjection{}).reduceUserCreated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -87,19 +86,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user MachineAddedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineAddedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineAddedEventType, + user.AggregateType, + []byte(`{ "userName": "machine-added" }`), - ), user.MachineAddedEventMapper), + ), user.MachineAddedEventMapper), }, reduce: (&loginNameProjection{}).reduceUserCreated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -118,17 +117,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user UserRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&loginNameProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -145,19 +144,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user UserUserNameChangedType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserUserNameChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserUserNameChangedType, + user.AggregateType, + []byte(`{ "userName": "changed" }`), - ), user.UsernameChangedEventMapper), + ), user.UsernameChangedEventMapper), }, reduce: (&loginNameProjection{}).reduceUserNameChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -175,19 +174,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "user UserDomainClaimedType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserDomainClaimedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserDomainClaimedType, + user.AggregateType, + []byte(`{ "userName": "claimed" }`), - ), user.DomainClaimedEventMapper), + ), user.DomainClaimedEventMapper), }, reduce: (&loginNameProjection{}).reduceUserDomainClaimed, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -205,19 +204,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainPolicyAddedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyAddedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.DomainPolicyAddedEventType, + user.AggregateType, + []byte(`{ "userLoginMustBeDomain": true }`), - ), org.DomainPolicyAddedEventMapper), + ), org.DomainPolicyAddedEventMapper), }, reduce: (&loginNameProjection{}).reduceOrgIAMPolicyAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -236,19 +235,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainPolicyChangedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyChangedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.DomainPolicyChangedEventType, + user.AggregateType, + []byte(`{ "userLoginMustBeDomain": false }`), - ), org.DomainPolicyChangedEventMapper), + ), org.DomainPolicyChangedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainPolicyChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -266,17 +265,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainPolicyChangedEventType no change", args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyChangedEventType), - user.AggregateType, - []byte(`{}`), - ), org.DomainPolicyChangedEventMapper), + event: getEvent( + testEvent( + org.DomainPolicyChangedEventType, + user.AggregateType, + []byte(`{}`), + ), org.DomainPolicyChangedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainPolicyChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -285,17 +284,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainPolicyRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.DomainPolicyRemovedEventType), - user.AggregateType, - []byte(`{}`), - ), org.DomainPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.DomainPolicyRemovedEventType, + user.AggregateType, + []byte(`{}`), + ), org.DomainPolicyRemovedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainPolicyRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -312,19 +311,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainVerifiedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainVerifiedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.OrgDomainVerifiedEventType, + user.AggregateType, + []byte(`{ "domain": "verified" }`), - ), org.DomainVerifiedEventMapper), + ), org.DomainVerifiedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainVerified, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -342,19 +341,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainRemovedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.OrgDomainRemovedEventType, + user.AggregateType, + []byte(`{ "domain": "remove" }`), - ), org.DomainRemovedEventMapper), + ), org.DomainRemovedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -372,19 +371,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "org OrgDomainPrimarySetEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainPrimarySetEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.OrgDomainPrimarySetEventType, + user.AggregateType, + []byte(`{ "domain": "primary" }`), - ), org.DomainPrimarySetEventMapper), + ), org.DomainPrimarySetEventMapper), }, reduce: (&loginNameProjection{}).reducePrimaryDomainSet, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -412,19 +411,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "iam OrgDomainPolicyAddedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DomainPolicyAddedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DomainPolicyAddedEventType, + user.AggregateType, + []byte(`{ "userLoginMustBeDomain": true }`), - ), instance.DomainPolicyAddedEventMapper), + ), instance.DomainPolicyAddedEventMapper), }, reduce: (&loginNameProjection{}).reduceOrgIAMPolicyAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -443,19 +442,19 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "iam OrgDomainPolicyChangedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DomainPolicyChangedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.DomainPolicyChangedEventType, + user.AggregateType, + []byte(`{ "userLoginMustBeDomain": false }`), - ), instance.DomainPolicyChangedEventMapper), + ), instance.DomainPolicyChangedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainPolicyChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -473,17 +472,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "iam OrgDomainPolicyChangedEventType no change", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.DomainPolicyChangedEventType), - user.AggregateType, - []byte(`{}`), - ), instance.DomainPolicyChangedEventMapper), + event: getEvent( + testEvent( + instance.DomainPolicyChangedEventType, + user.AggregateType, + []byte(`{}`), + ), instance.DomainPolicyChangedEventMapper), }, reduce: (&loginNameProjection{}).reduceDomainPolicyChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -492,17 +491,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: (&loginNameProjection{}).reduceInstanceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -530,17 +529,17 @@ func TestLoginNameProjection_reduces(t *testing.T) { { name: "instance.reduceOwnerRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - []byte(`{"name": "Name"}`), - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + []byte(`{"name": "Name"}`), + ), org.OrgRemovedEventMapper), }, reduce: (&loginNameProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/login_policy.go b/internal/query/projection/login_policy.go index f05dce526..3101accbe 100644 --- a/internal/query/projection/login_policy.go +++ b/internal/query/projection/login_policy.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -43,56 +43,57 @@ const ( LoginPolicyOwnerRemovedCol = "owner_removed" ) -type loginPolicyProjection struct { - crdb.StatementHandler +type loginPolicyProjection struct{} + +func newLoginPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(loginPolicyProjection)) } -func newLoginPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *loginPolicyProjection { - p := new(loginPolicyProjection) - config.ProjectionName = LoginPolicyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(LoginPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(LoginPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LoginPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(LoginPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LoginPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LoginPolicyAllowRegisterCol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginPolicyAllowUsernamePasswordCol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginPolicyAllowExternalIDPsCol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginPolicyForceMFACol, crdb.ColumnTypeBool), - crdb.NewColumn(LoginPolicyForceMFALocalOnlyCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(LoginPolicy2FAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()), - crdb.NewColumn(LoginPolicyMFAsCol, crdb.ColumnTypeEnumArray, crdb.Nullable()), - crdb.NewColumn(LoginPolicyPasswordlessTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(LoginPolicyHidePWResetCol, crdb.ColumnTypeBool), - crdb.NewColumn(IgnoreUnknownUsernames, crdb.ColumnTypeBool), - crdb.NewColumn(AllowDomainDiscovery, crdb.ColumnTypeBool), - crdb.NewColumn(DisableLoginWithEmail, crdb.ColumnTypeBool), - crdb.NewColumn(DisableLoginWithPhone, crdb.ColumnTypeBool), - crdb.NewColumn(DefaultRedirectURI, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(PasswordCheckLifetimeCol, crdb.ColumnTypeInt64), - crdb.NewColumn(ExternalLoginCheckLifetimeCol, crdb.ColumnTypeInt64), - crdb.NewColumn(MFAInitSkipLifetimeCol, crdb.ColumnTypeInt64), - crdb.NewColumn(SecondFactorCheckLifetimeCol, crdb.ColumnTypeInt64), - crdb.NewColumn(MultiFactorCheckLifetimeCol, crdb.ColumnTypeInt64), - crdb.NewColumn(LoginPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*loginPolicyProjection) Name() string { + return LoginPolicyTable +} + +func (*loginPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(LoginPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(LoginPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LoginPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(LoginPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(LoginPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LoginPolicyAllowRegisterCol, handler.ColumnTypeBool), + handler.NewColumn(LoginPolicyAllowUsernamePasswordCol, handler.ColumnTypeBool), + handler.NewColumn(LoginPolicyAllowExternalIDPsCol, handler.ColumnTypeBool), + handler.NewColumn(LoginPolicyForceMFACol, handler.ColumnTypeBool), + handler.NewColumn(LoginPolicyForceMFALocalOnlyCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(LoginPolicy2FAsCol, handler.ColumnTypeEnumArray, handler.Nullable()), + handler.NewColumn(LoginPolicyMFAsCol, handler.ColumnTypeEnumArray, handler.Nullable()), + handler.NewColumn(LoginPolicyPasswordlessTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(LoginPolicyHidePWResetCol, handler.ColumnTypeBool), + handler.NewColumn(IgnoreUnknownUsernames, handler.ColumnTypeBool), + handler.NewColumn(AllowDomainDiscovery, handler.ColumnTypeBool), + handler.NewColumn(DisableLoginWithEmail, handler.ColumnTypeBool), + handler.NewColumn(DisableLoginWithPhone, handler.ColumnTypeBool), + handler.NewColumn(DefaultRedirectURI, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(PasswordCheckLifetimeCol, handler.ColumnTypeInt64), + handler.NewColumn(ExternalLoginCheckLifetimeCol, handler.ColumnTypeInt64), + handler.NewColumn(MFAInitSkipLifetimeCol, handler.ColumnTypeInt64), + handler.NewColumn(SecondFactorCheckLifetimeCol, handler.ColumnTypeInt64), + handler.NewColumn(MultiFactorCheckLifetimeCol, handler.ColumnTypeInt64), + handler.NewColumn(LoginPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(LoginPolicyInstanceIDCol, LoginPolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{LoginPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(LoginPolicyInstanceIDCol, LoginPolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{LoginPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *loginPolicyProjection) reducers() []handler.AggregateReducer { +func (p *loginPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.LoginPolicyAddedEventType, Reduce: p.reduceLoginPolicyAdded, @@ -129,7 +130,7 @@ func (p *loginPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.LoginPolicyAddedEventType, Reduce: p.reduceLoginPolicyAdded, @@ -177,7 +178,7 @@ func (p *loginPolicyProjection) reduceLoginPolicyAdded(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-pYPxS", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyAddedEventType, instance.LoginPolicyAddedEventType}) } - return crdb.NewCreateStatement(&policyEvent, []handler.Column{ + return handler.NewCreateStatement(&policyEvent, []handler.Column{ handler.NewCol(LoginPolicyIDCol, policyEvent.Aggregate().ID), handler.NewCol(LoginPolicyInstanceIDCol, policyEvent.Aggregate().InstanceID), handler.NewCol(LoginPolicyCreationDateCol, policyEvent.CreationDate()), @@ -271,7 +272,7 @@ func (p *loginPolicyProjection) reduceLoginPolicyChanged(event eventstore.Event) cols = append(cols, handler.NewCol(MultiFactorCheckLifetimeCol, *policyEvent.MultiFactorCheckLifetime)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -292,12 +293,12 @@ func (p *loginPolicyProjection) reduceMFAAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-WMhAV", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyMultiFactorAddedEventType, instance.LoginPolicyMultiFactorAddedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, []handler.Column{ handler.NewCol(LoginPolicyChangeDateCol, policyEvent.CreationDate()), handler.NewCol(LoginPolicySequenceCol, policyEvent.Sequence()), - crdb.NewArrayAppendCol(LoginPolicyMFAsCol, policyEvent.MFAType), + handler.NewArrayAppendCol(LoginPolicyMFAsCol, policyEvent.MFAType), }, []handler.Condition{ handler.NewCond(LoginPolicyIDCol, policyEvent.Aggregate().ID), @@ -317,12 +318,12 @@ func (p *loginPolicyProjection) reduceMFARemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-czU7n", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicyMultiFactorRemovedEventType, instance.LoginPolicyMultiFactorRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, []handler.Column{ handler.NewCol(LoginPolicyChangeDateCol, policyEvent.CreationDate()), handler.NewCol(LoginPolicySequenceCol, policyEvent.Sequence()), - crdb.NewArrayRemoveCol(LoginPolicyMFAsCol, policyEvent.MFAType), + handler.NewArrayRemoveCol(LoginPolicyMFAsCol, policyEvent.MFAType), }, []handler.Condition{ handler.NewCond(LoginPolicyIDCol, policyEvent.Aggregate().ID), @@ -336,7 +337,7 @@ func (p *loginPolicyProjection) reduceLoginPolicyRemoved(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-oRSvD", "reduce.wrong.event.type %s", org.LoginPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(LoginPolicyIDCol, e.Aggregate().ID), @@ -356,12 +357,12 @@ func (p *loginPolicyProjection) reduceSecondFactorAdded(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-agB2E", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicySecondFactorAddedEventType, instance.LoginPolicySecondFactorAddedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, []handler.Column{ handler.NewCol(LoginPolicyChangeDateCol, policyEvent.CreationDate()), handler.NewCol(LoginPolicySequenceCol, policyEvent.Sequence()), - crdb.NewArrayAppendCol(LoginPolicy2FAsCol, policyEvent.MFAType), + handler.NewArrayAppendCol(LoginPolicy2FAsCol, policyEvent.MFAType), }, []handler.Condition{ handler.NewCond(LoginPolicyIDCol, policyEvent.Aggregate().ID), @@ -381,12 +382,12 @@ func (p *loginPolicyProjection) reduceSecondFactorRemoved(event eventstore.Event return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-KYJvA", "reduce.wrong.event.type %v", []eventstore.EventType{org.LoginPolicySecondFactorRemovedEventType, instance.LoginPolicySecondFactorRemovedEventType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, []handler.Column{ handler.NewCol(LoginPolicyChangeDateCol, policyEvent.CreationDate()), handler.NewCol(LoginPolicySequenceCol, policyEvent.Sequence()), - crdb.NewArrayRemoveCol(LoginPolicy2FAsCol, policyEvent.MFAType), + handler.NewArrayRemoveCol(LoginPolicy2FAsCol, policyEvent.MFAType), }, []handler.Condition{ handler.NewCond(LoginPolicyIDCol, policyEvent.Aggregate().ID), @@ -401,7 +402,7 @@ func (p *loginPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-B8NZW", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(LoginPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/login_policy_test.go b/internal/query/projection/login_policy_test.go index 62df313b7..4ebfa3483 100644 --- a/internal/query/projection/login_policy_test.go +++ b/internal/query/projection/login_policy_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -26,10 +25,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { { name: "org reduceLoginPolicyAdded without forceMFALocalOnly", args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyAddedEventType, + org.AggregateType, + []byte(`{ "allowUsernamePassword": true, "allowRegister": true, "allowExternalIdp": false, @@ -47,13 +47,12 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { "secondFactorCheckLifetime": 10000000, "multiFactorCheckLifetime": 10000000 }`), - ), org.LoginPolicyAddedEventMapper), + ), org.LoginPolicyAddedEventMapper), }, reduce: (&loginPolicyProjection{}).reduceLoginPolicyAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -92,7 +91,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceLoginPolicyAdded", args: args{ event: getEvent(testEvent( - repository.EventType(org.LoginPolicyAddedEventType), + org.LoginPolicyAddedEventType, org.AggregateType, []byte(`{ "allowUsernamePassword": true, @@ -117,9 +116,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { }, reduce: (&loginPolicyProjection{}).reduceLoginPolicyAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -158,10 +156,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceLoginPolicyChanged", reduce: (&loginPolicyProjection{}).reduceLoginPolicyChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyChangedEventType, + org.AggregateType, + []byte(`{ "allowUsernamePassword": true, "allowRegister": true, "allowExternalIdp": true, @@ -180,12 +179,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { "secondFactorCheckLifetime": 10000000, "multiFactorCheckLifetime": 10000000 }`), - ), org.LoginPolicyChangedEventMapper), + ), org.LoginPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -222,18 +220,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceMFAAdded", reduce: (&loginPolicyProjection{}).reduceMFAAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyMultiFactorAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyMultiFactorAddedEventType, + org.AggregateType, + []byte(`{ "mfaType": 1 }`), - ), org.MultiFactorAddedEventEventMapper), + ), org.MultiFactorAddedEventEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -254,18 +252,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceMFARemoved", reduce: (&loginPolicyProjection{}).reduceMFARemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyMultiFactorRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicyMultiFactorRemovedEventType, + org.AggregateType, + []byte(`{ "mfaType": 1 }`), - ), org.MultiFactorRemovedEventEventMapper), + ), org.MultiFactorRemovedEventEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -286,16 +284,16 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceLoginPolicyRemoved", reduce: (&loginPolicyProjection{}).reduceLoginPolicyRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.LoginPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.LoginPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.LoginPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -313,18 +311,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceSecondFactorAdded", reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicySecondFactorAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicySecondFactorAddedEventType, + org.AggregateType, + []byte(`{ "mfaType": 2 }`), - ), org.SecondFactorAddedEventEventMapper), + ), org.SecondFactorAddedEventEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -345,18 +343,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org reduceSecondFactorRemoved", reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.LoginPolicySecondFactorRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.LoginPolicySecondFactorRemovedEventType, + org.AggregateType, + []byte(`{ "mfaType": 2 }`), - ), org.SecondFactorRemovedEventEventMapper), + ), org.SecondFactorRemovedEventEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -377,10 +375,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceLoginPolicyAdded", reduce: (&loginPolicyProjection{}).reduceLoginPolicyAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "allowUsernamePassword": true, "allowRegister": true, "allowExternalIdp": true, @@ -399,12 +398,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { "secondFactorCheckLifetime": 10000000, "multiFactorCheckLifetime": 10000000 }`), - ), instance.LoginPolicyAddedEventMapper), + ), instance.LoginPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -443,10 +441,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceLoginPolicyChanged", reduce: (&loginPolicyProjection{}).reduceLoginPolicyChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "allowUsernamePassword": true, "allowRegister": true, "allowExternalIdp": true, @@ -460,12 +459,11 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { "passwordlessType": 1, "defaultRedirectURI": "https://example.com/redirect" }`), - ), instance.LoginPolicyChangedEventMapper), + ), instance.LoginPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -497,18 +495,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceMFAAdded", reduce: (&loginPolicyProjection{}).reduceMFAAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyMultiFactorAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyMultiFactorAddedEventType, + instance.AggregateType, + []byte(`{ "mfaType": 1 }`), - ), instance.MultiFactorAddedEventMapper), + ), instance.MultiFactorAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -529,18 +527,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceMFARemoved", reduce: (&loginPolicyProjection{}).reduceMFARemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicyMultiFactorRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicyMultiFactorRemovedEventType, + instance.AggregateType, + []byte(`{ "mfaType": 1 }`), - ), instance.MultiFactorRemovedEventMapper), + ), instance.MultiFactorRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -561,18 +559,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceSecondFactorAdded u2f", reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicySecondFactorAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicySecondFactorAddedEventType, + instance.AggregateType, + []byte(`{ "mfaType": 2 }`), - ), instance.SecondFactorAddedEventMapper), + ), instance.SecondFactorAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -593,18 +591,18 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "instance reduceSecondFactorRemoved u2f", reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.LoginPolicySecondFactorRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.LoginPolicySecondFactorRemovedEventType, + instance.AggregateType, + []byte(`{ "mfaType": 2 }`), - ), instance.SecondFactorRemovedEventMapper), + ), instance.SecondFactorRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -626,7 +624,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { reduce: (&loginPolicyProjection{}).reduceSecondFactorAdded, args: args{ event: getEvent(testEvent( - repository.EventType(instance.LoginPolicySecondFactorAddedEventType), + instance.LoginPolicySecondFactorAddedEventType, instance.AggregateType, []byte(`{ "mfaType": 3 @@ -634,9 +632,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { ), instance.SecondFactorAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -658,7 +655,7 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { reduce: (&loginPolicyProjection{}).reduceSecondFactorRemoved, args: args{ event: getEvent(testEvent( - repository.EventType(instance.LoginPolicySecondFactorRemovedEventType), + instance.LoginPolicySecondFactorRemovedEventType, instance.AggregateType, []byte(`{ "mfaType": 3 @@ -666,9 +663,8 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { ), instance.SecondFactorRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -689,16 +685,16 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&loginPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -715,17 +711,17 @@ func TestLoginPolicyProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(LoginPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/mail_template.go b/internal/query/projection/mail_template.go index d88f00054..91bab4c65 100644 --- a/internal/query/projection/mail_template.go +++ b/internal/query/projection/mail_template.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -27,39 +27,40 @@ const ( MailTemplateOwnerRemovedCol = "owner_removed" ) -type mailTemplateProjection struct { - crdb.StatementHandler +type mailTemplateProjection struct{} + +func newMailTemplateProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(mailTemplateProjection)) } -func newMailTemplateProjection(ctx context.Context, config crdb.StatementHandlerConfig) *mailTemplateProjection { - p := new(mailTemplateProjection) - config.ProjectionName = MailTemplateTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(MailTemplateAggregateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MailTemplateInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MailTemplateCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MailTemplateChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MailTemplateSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(MailTemplateStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(MailTemplateIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(MailTemplateTemplateCol, crdb.ColumnTypeBytes), - crdb.NewColumn(MailTemplateOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*mailTemplateProjection) Name() string { + return MailTemplateTable +} + +func (*mailTemplateProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(MailTemplateAggregateIDCol, handler.ColumnTypeText), + handler.NewColumn(MailTemplateInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(MailTemplateCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(MailTemplateChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(MailTemplateSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(MailTemplateStateCol, handler.ColumnTypeEnum), + handler.NewColumn(MailTemplateIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(MailTemplateTemplateCol, handler.ColumnTypeBytes), + handler.NewColumn(MailTemplateOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(MailTemplateInstanceIDCol, MailTemplateAggregateIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{MailTemplateOwnerRemovedCol})), + handler.NewPrimaryKey(MailTemplateInstanceIDCol, MailTemplateAggregateIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{MailTemplateOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *mailTemplateProjection) reducers() []handler.AggregateReducer { +func (p *mailTemplateProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.MailTemplateAddedEventType, Reduce: p.reduceAdded, @@ -80,7 +81,7 @@ func (p *mailTemplateProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.MailTemplateAddedEventType, Reduce: p.reduceAdded, @@ -111,7 +112,7 @@ func (p *mailTemplateProjection) reduceAdded(event eventstore.Event) (*handler.S default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-0pJ3f", "reduce.wrong.event.type, %v", []eventstore.EventType{org.MailTemplateAddedEventType, instance.MailTemplateAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &templateEvent, []handler.Column{ handler.NewCol(MailTemplateAggregateIDCol, templateEvent.Aggregate().ID), @@ -142,7 +143,7 @@ func (p *mailTemplateProjection) reduceChanged(event eventstore.Event) (*handler if policyEvent.Template != nil { cols = append(cols, handler.NewCol(MailTemplateTemplateCol, *policyEvent.Template)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -156,7 +157,7 @@ func (p *mailTemplateProjection) reduceRemoved(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-3jJGs", "reduce.wrong.event.type %s", org.MailTemplateRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(MailTemplateAggregateIDCol, policyEvent.Aggregate().ID), @@ -170,7 +171,7 @@ func (p *mailTemplateProjection) reduceOwnerRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-CThXR", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(MailTemplateInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/mail_template_test.go b/internal/query/projection/mail_template_test.go index f4a7d46b0..336ad394f 100644 --- a/internal/query/projection/mail_template_test.go +++ b/internal/query/projection/mail_template_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,19 +24,19 @@ func TestMailTemplateProjection_reduces(t *testing.T) { { name: "org.reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MailTemplateAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MailTemplateAddedEventType, + org.AggregateType, + []byte(`{ "template": "PHRhYmxlPjwvdGFibGU+" }`), - ), org.MailTemplateAddedEventMapper), + ), org.MailTemplateAddedEventMapper), }, reduce: (&mailTemplateProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -61,18 +60,18 @@ func TestMailTemplateProjection_reduces(t *testing.T) { name: "org.reduceChanged", reduce: (&mailTemplateProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.MailTemplateChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MailTemplateChangedEventType, + org.AggregateType, + []byte(`{ "template": "PHRhYmxlPjwvdGFibGU+" }`), - ), org.MailTemplateChangedEventMapper), + ), org.MailTemplateChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -93,16 +92,16 @@ func TestMailTemplateProjection_reduces(t *testing.T) { name: "org.reduceRemoved", reduce: (&mailTemplateProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.MailTemplateRemovedEventType), - org.AggregateType, - nil, - ), org.MailTemplateRemovedEventMapper), + event: getEvent( + testEvent( + org.MailTemplateRemovedEventType, + org.AggregateType, + nil, + ), org.MailTemplateRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -119,17 +118,17 @@ func TestMailTemplateProjection_reduces(t *testing.T) { { name: "instance.reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MailTemplateInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -146,18 +145,18 @@ func TestMailTemplateProjection_reduces(t *testing.T) { name: "instance.reduceAdded", reduce: (&mailTemplateProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MailTemplateAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MailTemplateAddedEventType, + instance.AggregateType, + []byte(`{ "template": "PHRhYmxlPjwvdGFibGU+" }`), - ), instance.MailTemplateAddedEventMapper), + ), instance.MailTemplateAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -181,18 +180,18 @@ func TestMailTemplateProjection_reduces(t *testing.T) { name: "instance.reduceChanged", reduce: (&mailTemplateProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.MailTemplateChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.MailTemplateChangedEventType, + instance.AggregateType, + []byte(`{ "template": "PHRhYmxlPjwvdGFibGU+" }`), - ), instance.MailTemplateChangedEventMapper), + ), instance.MailTemplateChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -213,16 +212,16 @@ func TestMailTemplateProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&mailTemplateProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/main_test.go b/internal/query/projection/main_test.go index 2cf61fe54..09754b140 100644 --- a/internal/query/projection/main_test.go +++ b/internal/query/projection/main_test.go @@ -25,7 +25,12 @@ func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore { for _, e := range expects { e(m) } - es := eventstore.NewEventstore(eventstore.TestConfig(m)) + es := eventstore.NewEventstore( + &eventstore.Config{ + Querier: m.MockQuerier, + Pusher: m.MockPusher, + }, + ) iam_repo.RegisterEventMappers(es) org.RegisterEventMappers(es) usr_repo.RegisterEventMappers(es) @@ -37,27 +42,18 @@ func eventstoreExpect(t *testing.T, expects ...expect) *eventstore.Eventstore { return es } -func expectFilter(events ...*repository.Event) expect { - return func(m *mock.MockRepository) { - m.ExpectFilterEvents(events...) - } -} - func eventFromEventPusher(event eventstore.Command) *repository.Event { data, _ := eventstore.EventData(event) return &repository.Event{ - ID: "", - Sequence: 0, - PreviousAggregateSequence: 0, - PreviousAggregateTypeSequence: 0, - CreationDate: time.Time{}, - Type: repository.EventType(event.Type()), - Data: data, - EditorService: event.EditorService(), - EditorUser: event.EditorUser(), - Version: repository.Version(event.Aggregate().Version), - AggregateID: event.Aggregate().ID, - AggregateType: repository.AggregateType(event.Aggregate().Type), - ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, + ID: "", + Seq: 0, + CreationDate: time.Time{}, + Typ: event.Type(), + Data: data, + EditorUser: event.Creator(), + Version: event.Aggregate().Version, + AggregateID: event.Aggregate().ID, + AggregateType: event.Aggregate().Type, + ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, } } diff --git a/internal/query/projection/member.go b/internal/query/projection/member.go index 3c6f0af18..364f6b1e0 100644 --- a/internal/query/projection/member.go +++ b/internal/query/projection/member.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/member" ) @@ -26,17 +25,17 @@ const ( ) var ( - memberColumns = []*crdb.Column{ - crdb.NewColumn(MemberCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MemberChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MemberUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MemberUserResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(MemberUserOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(MemberRolesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(MemberSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(MemberResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(MemberInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(MemberOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), + memberColumns = []*handler.InitColumn{ + handler.NewColumn(MemberCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(MemberChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(MemberUserIDCol, handler.ColumnTypeText), + handler.NewColumn(MemberUserResourceOwner, handler.ColumnTypeText), + handler.NewColumn(MemberUserOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(MemberRolesCol, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(MemberSequence, handler.ColumnTypeInt64), + handler.NewColumn(MemberResourceOwner, handler.ColumnTypeText), + handler.NewColumn(MemberInstanceID, handler.ColumnTypeText), + handler.NewColumn(MemberOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), } ) @@ -67,9 +66,9 @@ func reduceMemberAdded(e member.MemberAddedEvent, userResourceOwner string, opts handler.NewCol(MemberUserIDCol, e.UserID), handler.NewCol(MemberUserResourceOwner, userResourceOwner), handler.NewCol(MemberUserOwnerRemoved, false), - handler.NewCol(MemberRolesCol, database.StringArray(e.Roles)), - handler.NewCol(MemberCreationDate, e.CreationDate()), - handler.NewCol(MemberChangeDate, e.CreationDate()), + handler.NewCol(MemberRolesCol, database.TextArray[string](e.Roles)), + handler.NewCol(MemberCreationDate, e.CreatedAt()), + handler.NewCol(MemberChangeDate, e.CreatedAt()), handler.NewCol(MemberSequence, e.Sequence()), handler.NewCol(MemberResourceOwner, e.Aggregate().ResourceOwner), handler.NewCol(MemberInstanceID, e.Aggregate().InstanceID), @@ -80,14 +79,14 @@ func reduceMemberAdded(e member.MemberAddedEvent, userResourceOwner string, opts config = opt(config) } - return crdb.NewCreateStatement(&e, config.cols), nil + return handler.NewCreateStatement(&e, config.cols), nil } func reduceMemberChanged(e member.MemberChangedEvent, opts ...reduceMemberOpt) (*handler.Statement, error) { config := reduceMemberConfig{ cols: []handler.Column{ - handler.NewCol(MemberRolesCol, database.StringArray(e.Roles)), - handler.NewCol(MemberChangeDate, e.CreationDate()), + handler.NewCol(MemberRolesCol, database.TextArray[string](e.Roles)), + handler.NewCol(MemberChangeDate, e.CreatedAt()), handler.NewCol(MemberSequence, e.Sequence()), }, conds: []handler.Condition{ @@ -99,7 +98,7 @@ func reduceMemberChanged(e member.MemberChangedEvent, opts ...reduceMemberOpt) ( config = opt(config) } - return crdb.NewUpdateStatement(&e, config.cols, config.conds), nil + return handler.NewUpdateStatement(&e, config.cols, config.conds), nil } func reduceMemberCascadeRemoved(e member.MemberCascadeRemovedEvent, opts ...reduceMemberOpt) (*handler.Statement, error) { @@ -112,7 +111,7 @@ func reduceMemberCascadeRemoved(e member.MemberCascadeRemovedEvent, opts ...redu for _, opt := range opts { config = opt(config) } - return crdb.NewDeleteStatement(&e, config.conds), nil + return handler.NewDeleteStatement(&e, config.conds), nil } func reduceMemberRemoved(e eventstore.Event, opts ...reduceMemberOpt) (*handler.Statement, error) { @@ -125,10 +124,10 @@ func reduceMemberRemoved(e eventstore.Event, opts ...reduceMemberOpt) (*handler. for _, opt := range opts { config = opt(config) } - return crdb.NewDeleteStatement(e, config.conds), nil + return handler.NewDeleteStatement(e, config.conds), nil } -func multiReduceMemberOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) func(eventstore.Event) crdb.Exec { +func multiReduceMemberOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) func(eventstore.Event) handler.Exec { config := reduceMemberConfig{ conds: []handler.Condition{ handler.NewCond(MemberInstanceID, e.Aggregate().InstanceID), @@ -140,7 +139,7 @@ func multiReduceMemberOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) config = opt(config) } - return crdb.AddDeleteStatement( + return handler.AddDeleteStatement( config.conds, ) } @@ -161,25 +160,25 @@ func memberUserOwnerRemovedConds(e eventstore.Event, opts ...reduceMemberOpt) [] func memberUserOwnerRemovedCols(e eventstore.Event) []handler.Column { return []handler.Column{ - handler.NewCol(MemberChangeDate, e.CreationDate()), + handler.NewCol(MemberChangeDate, e.CreatedAt()), handler.NewCol(MemberSequence, e.Sequence()), handler.NewCol(MemberUserOwnerRemoved, true), } } func reduceMemberUserOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) (*handler.Statement, error) { - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, memberUserOwnerRemovedConds(e, opts...), ), nil } -func multiReduceMemberUserOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) func(eventstore.Event) crdb.Exec { - return crdb.AddDeleteStatement( +func multiReduceMemberUserOwnerRemoved(e eventstore.Event, opts ...reduceMemberOpt) func(eventstore.Event) handler.Exec { + return handler.AddDeleteStatement( memberUserOwnerRemovedConds(e, opts...), ) } -func setMemberContext(event eventstore.Aggregate) context.Context { - return authz.WithInstanceID(context.Background(), event.InstanceID) +func setMemberContext(aggregate *eventstore.Aggregate) context.Context { + return authz.WithInstanceID(context.Background(), aggregate.InstanceID) } diff --git a/internal/query/projection/message_text_test.go b/internal/query/projection/message_text_test.go index 695829dde..0fb3f1441 100644 --- a/internal/query/projection/message_text_test.go +++ b/internal/query/projection/message_text_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,22 +24,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded Title", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Title", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -64,22 +63,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded PreHeader", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "PreHeader", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -103,22 +102,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded Subject", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Subject", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -142,22 +141,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded Greeting", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Greeting", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -181,22 +180,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded Text", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -220,22 +219,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded ButtonText", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "ButtonText", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -259,22 +258,22 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceAdded Footer", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextSetEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextSetEventType, + org.AggregateType, + []byte(`{ "key": "Footer", "language": "en", "template": "InitCode", "text": "Test" }`), - ), org.CustomTextSetEventMapper), + ), org.CustomTextSetEventMapper), }, reduce: (&messageTextProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -298,21 +297,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved Title", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Title", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -334,17 +333,17 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MessageTextInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -360,21 +359,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved PreHeader", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "PreHeader", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -396,21 +395,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved Subject", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Subject", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -432,21 +431,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved Greeting", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Greeting", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -468,21 +467,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved Text", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Text", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -504,21 +503,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved ButtonText", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "ButtonText", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -540,21 +539,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "org reduceRemoved Footer", args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Footer", "language": "en", "template": "InitCode" }`), - ), org.CustomTextRemovedEventMapper), + ), org.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -577,20 +576,20 @@ func TestMessageTextProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&messageTextProjection{}).reduceTemplateRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.CustomTextTemplateRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.CustomTextTemplateRemovedEventType, + org.AggregateType, + []byte(`{ "key": "Title", "language": "en", "template": "InitCode" }`), - ), org.CustomTextTemplateRemovedEventMapper), + ), org.CustomTextTemplateRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -610,21 +609,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&messageTextProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.CustomTextSetEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.CustomTextSetEventType, + instance.AggregateType, + []byte(`{ "key": "Title", "language": "en", "template": "InitCode", "text": "Test" }`), - ), instance.CustomTextSetEventMapper), + ), instance.CustomTextSetEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -648,21 +647,21 @@ func TestMessageTextProjection_reduces(t *testing.T) { { name: "instance reduceRemoved Title", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.CustomTextRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.CustomTextRemovedEventType, + instance.AggregateType, + []byte(`{ "key": "Title", "language": "en", "template": "InitCode" }`), - ), instance.CustomTextRemovedEventMapper), + ), instance.CustomTextRemovedEventMapper), }, reduce: (&messageTextProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -685,16 +684,16 @@ func TestMessageTextProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&messageTextProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/message_texts.go b/internal/query/projection/message_texts.go index 43ede0d07..fd6af6c1a 100644 --- a/internal/query/projection/message_texts.go +++ b/internal/query/projection/message_texts.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -34,46 +34,47 @@ const ( MessageTextOwnerRemovedCol = "owner_removed" ) -type messageTextProjection struct { - crdb.StatementHandler +type messageTextProjection struct{} + +func newMessageTextProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(messageTextProjection)) } -func newMessageTextProjection(ctx context.Context, config crdb.StatementHandlerConfig) *messageTextProjection { - p := new(messageTextProjection) - config.ProjectionName = MessageTextTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(MessageTextAggregateIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MessageTextInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MessageTextCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MessageTextChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(MessageTextSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(MessageTextStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(MessageTextTypeCol, crdb.ColumnTypeText), - crdb.NewColumn(MessageTextLanguageCol, crdb.ColumnTypeText), - crdb.NewColumn(MessageTextTitleCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextPreHeaderCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextSubjectCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextGreetingCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextTextCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextButtonTextCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextFooterCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MessageTextOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*messageTextProjection) Name() string { + return MessageTextTable +} + +func (*messageTextProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(MessageTextAggregateIDCol, handler.ColumnTypeText), + handler.NewColumn(MessageTextInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(MessageTextCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(MessageTextChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(MessageTextSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(MessageTextStateCol, handler.ColumnTypeEnum), + handler.NewColumn(MessageTextTypeCol, handler.ColumnTypeText), + handler.NewColumn(MessageTextLanguageCol, handler.ColumnTypeText), + handler.NewColumn(MessageTextTitleCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextPreHeaderCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextSubjectCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextGreetingCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextTextCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextButtonTextCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextFooterCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MessageTextOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(MessageTextInstanceIDCol, MessageTextAggregateIDCol, MessageTextTypeCol, MessageTextLanguageCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{MessageTextOwnerRemovedCol})), + handler.NewPrimaryKey(MessageTextInstanceIDCol, MessageTextAggregateIDCol, MessageTextTypeCol, MessageTextLanguageCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{MessageTextOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *messageTextProjection) reducers() []handler.AggregateReducer { +func (p *messageTextProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.CustomTextSetEventType, Reduce: p.reduceAdded, @@ -94,7 +95,7 @@ func (p *messageTextProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.CustomTextSetEventType, Reduce: p.reduceAdded, @@ -127,7 +128,7 @@ func (p *messageTextProjection) reduceAdded(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-2n90r", "reduce.wrong.event.type %v", []eventstore.EventType{org.CustomTextSetEventType, instance.CustomTextSetEventType}) } if !isMessageTemplate(templateEvent.Template) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } cols := []handler.Column{ @@ -161,7 +162,7 @@ func (p *messageTextProjection) reduceAdded(event eventstore.Event) (*handler.St if isFooterText(templateEvent.Key) { cols = append(cols, handler.NewCol(MessageTextFooterCol, templateEvent.Text)) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( &templateEvent, []handler.Column{ handler.NewCol(MessageTextInstanceIDCol, nil), @@ -184,7 +185,7 @@ func (p *messageTextProjection) reduceRemoved(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-fm0ge", "reduce.wrong.event.type %v", []eventstore.EventType{org.CustomTextRemovedEventType, instance.CustomTextRemovedEventType}) } if !isMessageTemplate(templateEvent.Template) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } cols := []handler.Column{ handler.NewCol(MessageTextChangeDateCol, templateEvent.CreationDate()), @@ -211,7 +212,7 @@ func (p *messageTextProjection) reduceRemoved(event eventstore.Event) (*handler. if isFooterText(templateEvent.Key) { cols = append(cols, handler.NewCol(MessageTextFooterCol, "")) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &templateEvent, cols, []handler.Condition{ @@ -234,9 +235,9 @@ func (p *messageTextProjection) reduceTemplateRemoved(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-2n9rs", "reduce.wrong.event.type %s", org.CustomTextTemplateRemovedEventType) } if !isMessageTemplate(templateEvent.Template) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(MessageTextAggregateIDCol, templateEvent.Aggregate().ID), @@ -253,7 +254,7 @@ func (p *messageTextProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-mLsQw", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(MessageTextInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/milestones.go b/internal/query/projection/milestones.go index dbbf22a60..0ac5e8184 100644 --- a/internal/query/projection/milestones.go +++ b/internal/query/projection/milestones.go @@ -3,11 +3,11 @@ package projection import ( "context" "strconv" - "strings" + internal_authz "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/milestone" "github.com/zitadel/zitadel/internal/repository/project" @@ -26,34 +26,38 @@ const ( ) type milestoneProjection struct { - crdb.StatementHandler + systemUsers map[string]*internal_authz.SystemAPIUser } -func newMilestoneProjection(ctx context.Context, config crdb.StatementHandlerConfig) *milestoneProjection { - p := new(milestoneProjection) - config.ProjectionName = MilestonesProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(MilestoneColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(MilestoneColumnType, crdb.ColumnTypeEnum), - crdb.NewColumn(MilestoneColumnReachedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(MilestoneColumnPushedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(MilestoneColumnPrimaryDomain, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MilestoneColumnIgnoreClientIDs, crdb.ColumnTypeTextArray, crdb.Nullable()), +func newMilestoneProjection(ctx context.Context, config handler.Config, systemUsers map[string]*internal_authz.SystemAPIUser) *handler.Handler { + return handler.NewHandler(ctx, &config, &milestoneProjection{systemUsers: systemUsers}) +} + +func (*milestoneProjection) Name() string { + return MilestonesProjectionTable +} + +func (*milestoneProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(MilestoneColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(MilestoneColumnType, handler.ColumnTypeEnum), + handler.NewColumn(MilestoneColumnReachedDate, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(MilestoneColumnPushedDate, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(MilestoneColumnPrimaryDomain, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MilestoneColumnIgnoreClientIDs, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(MilestoneColumnInstanceID, MilestoneColumnType), + handler.NewPrimaryKey(MilestoneColumnInstanceID, MilestoneColumnType), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *milestoneProjection) reducers() []handler.AggregateReducer { +// Reducers implements handler.Projection. +func (p *milestoneProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceAddedEventType, Reduce: p.reduceInstanceAdded, @@ -70,7 +74,7 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer { }, { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.ProjectAddedType, Reduce: p.reduceProjectAdded, @@ -91,7 +95,7 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { // user.UserTokenAddedType is not emitted on creation of personal access tokens // PATs have no effect on milestone.AuthenticationSucceededOnApplication or milestone.AuthenticationSucceededOnInstance @@ -102,7 +106,7 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer { }, { Aggregate: milestone.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: milestone.PushedEventType, Reduce: p.reduceMilestonePushed, @@ -118,18 +122,18 @@ func (p *milestoneProjection) reduceInstanceAdded(event eventstore.Event) (*hand return nil, err } allTypes := milestone.AllTypes() - statements := make([]func(eventstore.Event) crdb.Exec, 0, len(allTypes)) + statements := make([]func(eventstore.Event) handler.Exec, 0, len(allTypes)) for _, msType := range allTypes { createColumns := []handler.Column{ handler.NewCol(MilestoneColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(MilestoneColumnType, msType), } if msType == milestone.InstanceCreated { - createColumns = append(createColumns, handler.NewCol(MilestoneColumnReachedDate, event.CreationDate())) + createColumns = append(createColumns, handler.NewCol(MilestoneColumnReachedDate, event.CreatedAt())) } - statements = append(statements, crdb.AddCreateStatement(createColumns)) + statements = append(statements, handler.AddCreateStatement(createColumns)) } - return crdb.NewMultiStatement(e, statements...), nil + return handler.NewMultiStatement(e, statements...), nil } func (p *milestoneProjection) reduceInstanceDomainPrimarySet(event eventstore.Event) (*handler.Statement, error) { @@ -137,14 +141,14 @@ func (p *milestoneProjection) reduceInstanceDomainPrimarySet(event eventstore.Ev if err != nil { return nil, err } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(MilestoneColumnPrimaryDomain, e.Domain), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, e.Aggregate().InstanceID), - crdb.NewIsNullCond(MilestoneColumnPushedDate), + handler.NewIsNullCond(MilestoneColumnPushedDate), }, ), nil } @@ -184,33 +188,33 @@ func (p *milestoneProjection) reduceUserTokenAdded(event eventstore.Event) (*han if err != nil { return nil, err } - statements := []func(eventstore.Event) crdb.Exec{ - crdb.AddUpdateStatement( + statements := []func(eventstore.Event) handler.Exec{ + handler.AddUpdateStatement( []handler.Column{ - handler.NewCol(MilestoneColumnReachedDate, event.CreationDate()), + handler.NewCol(MilestoneColumnReachedDate, event.CreatedAt()), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), handler.NewCond(MilestoneColumnType, milestone.AuthenticationSucceededOnInstance), - crdb.NewIsNullCond(MilestoneColumnReachedDate), + handler.NewIsNullCond(MilestoneColumnReachedDate), }, ), } // We ignore authentications without app, for example JWT profile or PAT if e.ApplicationID != "" { - statements = append(statements, crdb.AddUpdateStatement( + statements = append(statements, handler.AddUpdateStatement( []handler.Column{ - handler.NewCol(MilestoneColumnReachedDate, event.CreationDate()), + handler.NewCol(MilestoneColumnReachedDate, event.CreatedAt()), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), handler.NewCond(MilestoneColumnType, milestone.AuthenticationSucceededOnApplication), - crdb.Not(crdb.NewTextArrayContainsCond(MilestoneColumnIgnoreClientIDs, e.ApplicationID)), - crdb.NewIsNullCond(MilestoneColumnReachedDate), + handler.Not(handler.NewTextArrayContainsCond(MilestoneColumnIgnoreClientIDs, e.ApplicationID)), + handler.NewIsNullCond(MilestoneColumnReachedDate), }, )) } - return crdb.NewMultiStatement(e, statements...), nil + return handler.NewMultiStatement(e, statements...), nil } func (p *milestoneProjection) reduceInstanceRemoved(event eventstore.Event) (*handler.Statement, error) { @@ -226,10 +230,10 @@ func (p *milestoneProjection) reduceMilestonePushed(event eventstore.Event) (*ha return nil, err } if e.MilestoneType != milestone.InstanceDeleted { - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(MilestoneColumnPushedDate, event.CreationDate()), + handler.NewCol(MilestoneColumnPushedDate, event.CreatedAt()), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), @@ -237,7 +241,7 @@ func (p *milestoneProjection) reduceMilestonePushed(event eventstore.Event) (*ha }, ), nil } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), @@ -248,7 +252,7 @@ func (p *milestoneProjection) reduceMilestonePushed(event eventstore.Event) (*ha func (p *milestoneProjection) reduceReachedIfUserEventFunc(msType milestone.Type) func(event eventstore.Event) (*handler.Statement, error) { return func(event eventstore.Event) (*handler.Statement, error) { if p.isSystemEvent(event) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } return p.reduceReachedFunc(msType)(event) } @@ -256,40 +260,46 @@ func (p *milestoneProjection) reduceReachedIfUserEventFunc(msType milestone.Type func (p *milestoneProjection) reduceReachedFunc(msType milestone.Type) func(event eventstore.Event) (*handler.Statement, error) { return func(event eventstore.Event) (*handler.Statement, error) { - return crdb.NewUpdateStatement(event, []handler.Column{ - handler.NewCol(MilestoneColumnReachedDate, event.CreationDate()), + return handler.NewUpdateStatement(event, []handler.Column{ + handler.NewCol(MilestoneColumnReachedDate, event.CreatedAt()), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), handler.NewCond(MilestoneColumnType, msType), - crdb.NewIsNullCond(MilestoneColumnReachedDate), + handler.NewIsNullCond(MilestoneColumnReachedDate), }), nil } } func (p *milestoneProjection) reduceAppConfigAdded(event eventstore.Event, clientID string) (*handler.Statement, error) { if !p.isSystemEvent(event) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - crdb.NewArrayAppendCol(MilestoneColumnIgnoreClientIDs, clientID), + handler.NewArrayAppendCol(MilestoneColumnIgnoreClientIDs, clientID), }, []handler.Condition{ handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), handler.NewCond(MilestoneColumnType, milestone.AuthenticationSucceededOnApplication), - crdb.NewIsNullCond(MilestoneColumnReachedDate), + handler.NewIsNullCond(MilestoneColumnReachedDate), }, ), nil } func (p *milestoneProjection) isSystemEvent(event eventstore.Event) bool { - if userId, err := strconv.Atoi(event.EditorUser()); err == nil && userId > 0 { + if userId, err := strconv.Atoi(event.Creator()); err == nil && userId > 0 { return false } - lowerEditorService := strings.ToLower(event.EditorService()) - return lowerEditorService == "" || - lowerEditorService == "system" || - lowerEditorService == "system-api" + + // check if it is a hard coded event creator + for _, creator := range []string{"", "system", "OIDC", "LOGIN", "SYSTEM"} { + if creator == event.Creator() { + return true + } + } + + _, ok := p.systemUsers[event.Creator()] + return ok } diff --git a/internal/query/projection/milestones_test.go b/internal/query/projection/milestones_test.go index 5138fc157..fd606538a 100644 --- a/internal/query/projection/milestones_test.go +++ b/internal/query/projection/milestones_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/milestone" "github.com/zitadel/zitadel/internal/repository/project" @@ -30,7 +29,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceInstanceAdded", args: args{ event: getEvent(timedTestEvent( - repository.EventType(instance.InstanceAddedEventType), + instance.InstanceAddedEventType, instance.AggregateType, []byte(`{}`), now, @@ -38,9 +37,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceInstanceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -94,16 +92,15 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceInstancePrimaryDomainSet", args: args{ event: getEvent(testEvent( - repository.EventType(instance.InstanceDomainPrimarySetEventType), + instance.InstanceDomainPrimarySetEventType, instance.AggregateType, []byte(`{"domain": "my.domain"}`), ), instance.DomainPrimarySetEventMapper), }, reduce: (&milestoneProjection{}).reduceInstanceDomainPrimarySet, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -121,7 +118,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceProjectAdded", args: args{ event: getEvent(timedTestEvent( - repository.EventType(project.ProjectAddedType), + project.ProjectAddedType, project.AggregateType, []byte(`{}`), now, @@ -129,9 +126,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceProjectAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -150,7 +146,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceApplicationAdded", args: args{ event: getEvent(timedTestEvent( - repository.EventType(project.ApplicationAddedType), + project.ApplicationAddedType, project.AggregateType, []byte(`{}`), now, @@ -158,9 +154,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceApplicationAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -179,33 +174,31 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceOIDCConfigAdded user event", args: args{ event: getEvent(testEvent( - repository.EventType(project.OIDCConfigAddedType), + project.OIDCConfigAddedType, project.AggregateType, []byte(`{}`), ), project.OIDCConfigAddedEventMapper), }, reduce: (&milestoneProjection{}).reduceOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "reduceOIDCConfigAdded system event", args: args{ event: getEvent(toSystemEvent(testEvent( - repository.EventType(project.OIDCConfigAddedType), + project.OIDCConfigAddedType, project.AggregateType, []byte(`{"clientId": "client-id"}`), )), project.OIDCConfigAddedEventMapper), }, reduce: (&milestoneProjection{}).reduceOIDCConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -224,33 +217,31 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceAPIConfigAdded user event", args: args{ event: getEvent(testEvent( - repository.EventType(project.APIConfigAddedType), + project.APIConfigAddedType, project.AggregateType, []byte(`{}`), ), project.APIConfigAddedEventMapper), }, reduce: (&milestoneProjection{}).reduceAPIConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "reduceAPIConfigAdded system event", args: args{ event: getEvent(toSystemEvent(testEvent( - repository.EventType(project.APIConfigAddedType), + project.APIConfigAddedType, project.AggregateType, []byte(`{"clientId": "client-id"}`), )), project.APIConfigAddedEventMapper), }, reduce: (&milestoneProjection{}).reduceAPIConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -269,7 +260,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceUserTokenAdded", args: args{ event: getEvent(timedTestEvent( - repository.EventType(user.UserTokenAddedType), + user.UserTokenAddedType, user.AggregateType, []byte(`{"applicationId": "client-id"}`), now, @@ -277,9 +268,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceUserTokenAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ // TODO: This can be optimized to only use one statement with OR executions: []execution{ @@ -297,7 +287,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { now, "instance-id", milestone.AuthenticationSucceededOnApplication, - database.StringArray{"client-id"}, + database.TextArray[string]{"client-id"}, }, }, }, @@ -308,7 +298,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceInstanceRemoved", args: args{ event: getEvent(timedTestEvent( - repository.EventType(instance.InstanceRemovedEventType), + instance.InstanceRemovedEventType, instance.AggregateType, []byte(`{}`), now, @@ -316,9 +306,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceInstanceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -337,7 +326,7 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceMilestonePushed normal milestone", args: args{ event: getEvent(timedTestEvent( - repository.EventType(milestone.PushedEventType), + milestone.PushedEventType, milestone.AggregateType, []byte(`{"type": "ProjectCreated"}`), now, @@ -345,9 +334,8 @@ func TestMilestonesProjection_reduces(t *testing.T) { }, reduce: (&milestoneProjection{}).reduceMilestonePushed, want: wantReduce{ - aggregateType: eventstore.AggregateType("milestone"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("milestone"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -366,16 +354,15 @@ func TestMilestonesProjection_reduces(t *testing.T) { name: "reduceMilestonePushed instance deleted milestone", args: args{ event: getEvent(testEvent( - repository.EventType(milestone.PushedEventType), + milestone.PushedEventType, milestone.AggregateType, []byte(`{"type": "InstanceDeleted"}`), ), milestone.PushedEventMapper), }, reduce: (&milestoneProjection{}).reduceMilestonePushed, want: wantReduce{ - aggregateType: eventstore.AggregateType("milestone"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("milestone"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/notification_policy.go b/internal/query/projection/notification_policy.go index d064d4395..49c8618a2 100644 --- a/internal/query/projection/notification_policy.go +++ b/internal/query/projection/notification_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -28,39 +28,40 @@ const ( NotificationPolicyColumnOwnerRemoved = "owner_removed" ) -type notificationPolicyProjection struct { - crdb.StatementHandler +type notificationPolicyProjection struct{} + +func newNotificationPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(notificationPolicyProjection)) } -func newNotificationPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *notificationPolicyProjection { - p := new(notificationPolicyProjection) - config.ProjectionName = NotificationPolicyProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(NotificationPolicyColumnID, crdb.ColumnTypeText), - crdb.NewColumn(NotificationPolicyColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(NotificationPolicyColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(NotificationPolicyColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(NotificationPolicyColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(NotificationPolicyColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(NotificationPolicyColumnStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(NotificationPolicyColumnIsDefault, crdb.ColumnTypeBool), - crdb.NewColumn(NotificationPolicyColumnPasswordChange, crdb.ColumnTypeBool), - crdb.NewColumn(NotificationPolicyColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*notificationPolicyProjection) Name() string { + return NotificationPolicyProjectionTable +} + +func (*notificationPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(NotificationPolicyColumnID, handler.ColumnTypeText), + handler.NewColumn(NotificationPolicyColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(NotificationPolicyColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(NotificationPolicyColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(NotificationPolicyColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(NotificationPolicyColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(NotificationPolicyColumnStateCol, handler.ColumnTypeEnum), + handler.NewColumn(NotificationPolicyColumnIsDefault, handler.ColumnTypeBool), + handler.NewColumn(NotificationPolicyColumnPasswordChange, handler.ColumnTypeBool), + handler.NewColumn(NotificationPolicyColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(NotificationPolicyColumnInstanceID, NotificationPolicyColumnID), + handler.NewPrimaryKey(NotificationPolicyColumnInstanceID, NotificationPolicyColumnID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *notificationPolicyProjection) reducers() []handler.AggregateReducer { +func (p *notificationPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.NotificationPolicyAddedEventType, Reduce: p.reduceAdded, @@ -81,7 +82,7 @@ func (p *notificationPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(NotificationPolicyColumnInstanceID), @@ -112,7 +113,7 @@ func (p *notificationPolicyProjection) reduceAdded(event eventstore.Event) (*han default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-x02s1m", "reduce.wrong.event.type %v", []eventstore.EventType{org.NotificationPolicyAddedEventType, instance.NotificationPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(NotificationPolicyColumnCreationDate, policyEvent.CreationDate()), @@ -144,7 +145,7 @@ func (p *notificationPolicyProjection) reduceChanged(event eventstore.Event) (*h if policyEvent.PasswordChange != nil { cols = append(cols, handler.NewCol(NotificationPolicyColumnPasswordChange, *policyEvent.PasswordChange)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -158,7 +159,7 @@ func (p *notificationPolicyProjection) reduceRemoved(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Po2iso2", "reduce.wrong.event.type %s", org.NotificationPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(NotificationPolicyColumnID, policyEvent.Aggregate().ID), @@ -172,7 +173,7 @@ func (p *notificationPolicyProjection) reduceOwnerRemoved(event eventstore.Event return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-poxi9a", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(DomainPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/notification_policy_test.go b/internal/query/projection/notification_policy_test.go index b61fc6a44..b21f33b74 100644 --- a/internal/query/projection/notification_policy_test.go +++ b/internal/query/projection/notification_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,19 +24,19 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.NotificationPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.NotificationPolicyAddedEventType, + org.AggregateType, + []byte(`{ "passwordChange": true }`), - ), org.NotificationPolicyAddedEventMapper), + ), org.NotificationPolicyAddedEventMapper), }, reduce: (¬ificationPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -62,18 +61,18 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (¬ificationPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.NotificationPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.NotificationPolicyChangedEventType, + org.AggregateType, + []byte(`{ "passwordChange": true }`), - ), org.NotificationPolicyChangedEventMapper), + ), org.NotificationPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -94,16 +93,16 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (¬ificationPolicyProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.NotificationPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.NotificationPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.NotificationPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.NotificationPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -119,17 +118,17 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { }, { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(NotificationPolicyColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -146,18 +145,18 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (¬ificationPolicyProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.NotificationPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.NotificationPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "passwordChange": true }`), - ), instance.NotificationPolicyAddedEventMapper), + ), instance.NotificationPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -182,18 +181,18 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (¬ificationPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.NotificationPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.NotificationPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "passwordChange": true }`), - ), instance.NotificationPolicyChangedEventMapper), + ), instance.NotificationPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -214,16 +213,16 @@ func TestNotificationPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (¬ificationPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/oidc_settings.go b/internal/query/projection/oidc_settings.go index ee2545b55..fc53b5260 100644 --- a/internal/query/projection/oidc_settings.go +++ b/internal/query/projection/oidc_settings.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -25,39 +25,40 @@ const ( OIDCSettingsColumnRefreshTokenExpiration = "refresh_token_expiration" ) -type oidcSettingsProjection struct { - crdb.StatementHandler +type oidcSettingsProjection struct{} + +func newOIDCSettingsProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(oidcSettingsProjection)) } -func newOIDCSettingsProjection(ctx context.Context, config crdb.StatementHandlerConfig) *oidcSettingsProjection { - p := new(oidcSettingsProjection) - config.ProjectionName = OIDCSettingsProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(OIDCSettingsColumnAggregateID, crdb.ColumnTypeText), - crdb.NewColumn(OIDCSettingsColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OIDCSettingsColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OIDCSettingsColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(OIDCSettingsColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(OIDCSettingsColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(OIDCSettingsColumnAccessTokenLifetime, crdb.ColumnTypeInt64), - crdb.NewColumn(OIDCSettingsColumnIdTokenLifetime, crdb.ColumnTypeInt64), - crdb.NewColumn(OIDCSettingsColumnRefreshTokenIdleExpiration, crdb.ColumnTypeInt64), - crdb.NewColumn(OIDCSettingsColumnRefreshTokenExpiration, crdb.ColumnTypeInt64), +func (*oidcSettingsProjection) Name() string { + return OIDCSettingsProjectionTable +} + +func (*oidcSettingsProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(OIDCSettingsColumnAggregateID, handler.ColumnTypeText), + handler.NewColumn(OIDCSettingsColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OIDCSettingsColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OIDCSettingsColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(OIDCSettingsColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(OIDCSettingsColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(OIDCSettingsColumnAccessTokenLifetime, handler.ColumnTypeInt64), + handler.NewColumn(OIDCSettingsColumnIdTokenLifetime, handler.ColumnTypeInt64), + handler.NewColumn(OIDCSettingsColumnRefreshTokenIdleExpiration, handler.ColumnTypeInt64), + handler.NewColumn(OIDCSettingsColumnRefreshTokenExpiration, handler.ColumnTypeInt64), }, - crdb.NewPrimaryKey(OIDCSettingsColumnInstanceID, OIDCSettingsColumnAggregateID), + handler.NewPrimaryKey(OIDCSettingsColumnInstanceID, OIDCSettingsColumnAggregateID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *oidcSettingsProjection) reducers() []handler.AggregateReducer { +func (p *oidcSettingsProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.OIDCSettingsAddedEventType, Reduce: p.reduceOIDCSettingsAdded, @@ -80,7 +81,7 @@ func (p *oidcSettingsProjection) reduceOIDCSettingsAdded(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-f9nwf", "reduce.wrong.event.type %s", instance.OIDCSettingsAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(OIDCSettingsColumnAggregateID, e.Aggregate().ID), @@ -120,7 +121,7 @@ func (p *oidcSettingsProjection) reduceOIDCSettingsChanged(event eventstore.Even if e.RefreshTokenExpiration != nil { columns = append(columns, handler.NewCol(OIDCSettingsColumnRefreshTokenExpiration, *e.RefreshTokenExpiration)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, columns, []handler.Condition{ diff --git a/internal/query/projection/oidc_settings_test.go b/internal/query/projection/oidc_settings_test.go index 46c90987d..c4d5c7b8e 100644 --- a/internal/query/projection/oidc_settings_test.go +++ b/internal/query/projection/oidc_settings_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -24,17 +23,17 @@ func TestOIDCSettingsProjection_reduces(t *testing.T) { { name: "reduceOIDCSettingsChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OIDCSettingsChangedEventType), - instance.AggregateType, - []byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`), - ), instance.OIDCSettingsChangedEventMapper), + event: getEvent( + testEvent( + instance.OIDCSettingsChangedEventType, + instance.AggregateType, + []byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`), + ), instance.OIDCSettingsChangedEventMapper), }, reduce: (&oidcSettingsProjection{}).reduceOIDCSettingsChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -57,17 +56,17 @@ func TestOIDCSettingsProjection_reduces(t *testing.T) { { name: "reduceOIDCSettingsAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.OIDCSettingsAddedEventType), - instance.AggregateType, - []byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`), - ), instance.OIDCSettingsAddedEventMapper), + event: getEvent( + testEvent( + instance.OIDCSettingsAddedEventType, + instance.AggregateType, + []byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`), + ), instance.OIDCSettingsAddedEventMapper), }, reduce: (&oidcSettingsProjection{}).reduceOIDCSettingsAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -92,17 +91,17 @@ func TestOIDCSettingsProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(OIDCSettingsColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/org.go b/internal/query/projection/org.go index 8ba5ae496..9db19403c 100644 --- a/internal/query/projection/org.go +++ b/internal/query/projection/org.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -26,40 +26,42 @@ const ( OrgColumnDomain = "primary_domain" ) -type orgProjection struct { - crdb.StatementHandler +type orgProjection struct{} + +func (*orgProjection) Name() string { + return OrgProjectionTable } -func newOrgProjection(ctx context.Context, config crdb.StatementHandlerConfig) *orgProjection { - p := new(orgProjection) - config.ProjectionName = OrgProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(OrgColumnID, crdb.ColumnTypeText), - crdb.NewColumn(OrgColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(OrgColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(OrgColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(OrgColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(OrgColumnName, crdb.ColumnTypeText), - crdb.NewColumn(OrgColumnDomain, crdb.ColumnTypeText, crdb.Default("")), +func newOrgProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(orgProjection)) +} + +// Init implements [handler.initializer] +func (p *orgProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(OrgColumnID, handler.ColumnTypeText), + handler.NewColumn(OrgColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(OrgColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(OrgColumnState, handler.ColumnTypeEnum), + handler.NewColumn(OrgColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(OrgColumnName, handler.ColumnTypeText), + handler.NewColumn(OrgColumnDomain, handler.ColumnTypeText, handler.Default("")), }, - crdb.NewPrimaryKey(OrgColumnInstanceID, OrgColumnID), - crdb.WithIndex(crdb.NewIndex("domain", []string{OrgColumnDomain})), - crdb.WithIndex(crdb.NewIndex("name", []string{OrgColumnName})), + handler.NewPrimaryKey(OrgColumnInstanceID, OrgColumnID), + handler.WithIndex(handler.NewIndex("domain", []string{OrgColumnDomain})), + handler.WithIndex(handler.NewIndex("name", []string{OrgColumnName})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *orgProjection) reducers() []handler.AggregateReducer { +func (p *orgProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgAddedEventType, Reduce: p.reduceOrgAdded, @@ -88,7 +90,7 @@ func (p *orgProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(OrgColumnInstanceID), @@ -103,7 +105,7 @@ func (p *orgProjection) reduceOrgAdded(event eventstore.Event) (*handler.Stateme if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-uYq4r", "reduce.wrong.event.type %s", org.OrgAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(OrgColumnID, e.Aggregate().ID), @@ -124,9 +126,9 @@ func (p *orgProjection) reduceOrgChanged(event eventstore.Event) (*handler.State return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Bg8oM", "reduce.wrong.event.type %s", org.OrgChangedEventType) } if e.Name == "" { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgColumnChangeDate, e.CreationDate()), @@ -145,7 +147,7 @@ func (p *orgProjection) reduceOrgDeactivated(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-BApK4", "reduce.wrong.event.type %s", org.OrgDeactivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgColumnChangeDate, e.CreationDate()), @@ -164,7 +166,7 @@ func (p *orgProjection) reduceOrgReactivated(event eventstore.Event) (*handler.S if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-o37De", "reduce.wrong.event.type %s", org.OrgReactivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgColumnChangeDate, e.CreationDate()), @@ -183,7 +185,7 @@ func (p *orgProjection) reducePrimaryDomainSet(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-4TbKT", "reduce.wrong.event.type %s", org.OrgDomainPrimarySetEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgColumnChangeDate, e.CreationDate()), @@ -202,7 +204,7 @@ func (p *orgProjection) reduceOrgRemoved(event eventstore.Event) (*handler.State if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-DgMSg", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(OrgColumnID, e.Aggregate().ID), diff --git a/internal/query/projection/org_domain.go b/internal/query/projection/org_domain.go index 6436cab38..21e03e922 100644 --- a/internal/query/projection/org_domain.go +++ b/internal/query/projection/org_domain.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -27,40 +27,41 @@ const ( OrgDomainOwnerRemovedCol = "owner_removed" ) -type orgDomainProjection struct { - crdb.StatementHandler +type orgDomainProjection struct{} + +func newOrgDomainProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(orgDomainProjection)) } -func newOrgDomainProjection(ctx context.Context, config crdb.StatementHandlerConfig) *orgDomainProjection { - p := new(orgDomainProjection) - config.ProjectionName = OrgDomainTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(OrgDomainOrgIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OrgDomainInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(OrgDomainCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgDomainChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgDomainSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(OrgDomainDomainCol, crdb.ColumnTypeText), - crdb.NewColumn(OrgDomainIsVerifiedCol, crdb.ColumnTypeBool), - crdb.NewColumn(OrgDomainIsPrimaryCol, crdb.ColumnTypeBool), - crdb.NewColumn(OrgDomainValidationTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(OrgDomainOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*orgDomainProjection) Name() string { + return OrgDomainTable +} + +func (*orgDomainProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(OrgDomainOrgIDCol, handler.ColumnTypeText), + handler.NewColumn(OrgDomainInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(OrgDomainCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgDomainChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgDomainSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(OrgDomainDomainCol, handler.ColumnTypeText), + handler.NewColumn(OrgDomainIsVerifiedCol, handler.ColumnTypeBool), + handler.NewColumn(OrgDomainIsPrimaryCol, handler.ColumnTypeBool), + handler.NewColumn(OrgDomainValidationTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(OrgDomainOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(OrgDomainOrgIDCol, OrgDomainDomainCol, OrgDomainInstanceIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{OrgDomainOwnerRemovedCol})), + handler.NewPrimaryKey(OrgDomainOrgIDCol, OrgDomainDomainCol, OrgDomainInstanceIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{OrgDomainOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *orgDomainProjection) reducers() []handler.AggregateReducer { +func (p *orgDomainProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgDomainAddedEventType, Reduce: p.reduceDomainAdded, @@ -89,7 +90,7 @@ func (p *orgDomainProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(OrgDomainInstanceIDCol), @@ -104,7 +105,7 @@ func (p *orgDomainProjection) reduceDomainAdded(event eventstore.Event) (*handle if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-DM2DI", "reduce.wrong.event.type %s", org.OrgDomainAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(OrgDomainCreationDateCol, e.CreationDate()), @@ -125,7 +126,7 @@ func (p *orgDomainProjection) reduceDomainVerificationAdded(event eventstore.Eve if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-EBzyu", "reduce.wrong.event.type %s", org.OrgDomainVerificationAddedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgDomainChangeDateCol, e.CreationDate()), @@ -145,7 +146,7 @@ func (p *orgDomainProjection) reduceDomainVerified(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-3Rvkr", "reduce.wrong.event.type %s", org.OrgDomainVerifiedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgDomainChangeDateCol, e.CreationDate()), @@ -165,9 +166,9 @@ func (p *orgDomainProjection) reducePrimaryDomainSet(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-aIuei", "reduce.wrong.event.type %s", org.OrgDomainPrimarySetEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(OrgDomainChangeDateCol, e.CreationDate()), handler.NewCol(OrgDomainSequenceCol, e.Sequence()), @@ -179,7 +180,7 @@ func (p *orgDomainProjection) reducePrimaryDomainSet(event eventstore.Event) (*h handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(OrgDomainChangeDateCol, e.CreationDate()), handler.NewCol(OrgDomainSequenceCol, e.Sequence()), @@ -199,7 +200,7 @@ func (p *orgDomainProjection) reduceDomainRemoved(event eventstore.Event) (*hand if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-gh1Mx", "reduce.wrong.event.type %s", org.OrgDomainRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(OrgDomainDomainCol, e.Domain), @@ -215,7 +216,7 @@ func (p *orgDomainProjection) reduceOwnerRemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-dMUKJ", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/org_domain_test.go b/internal/query/projection/org_domain_test.go index 99c265ecf..0494177eb 100644 --- a/internal/query/projection/org_domain_test.go +++ b/internal/query/projection/org_domain_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,17 +24,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "reduceDomainAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainAddedEventType), - org.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), org.DomainAddedEventMapper), + event: getEvent( + testEvent( + org.OrgDomainAddedEventType, + org.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), org.DomainAddedEventMapper), }, reduce: (&orgDomainProjection{}).reduceDomainAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -59,17 +58,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "reduceDomainVerificationAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainVerificationAddedEventType), - org.AggregateType, - []byte(`{"domain": "domain.new", "validationType": 2}`), - ), org.DomainVerificationAddedEventMapper), + event: getEvent( + testEvent( + org.OrgDomainVerificationAddedEventType, + org.AggregateType, + []byte(`{"domain": "domain.new", "validationType": 2}`), + ), org.DomainVerificationAddedEventMapper), }, reduce: (&orgDomainProjection{}).reduceDomainVerificationAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -90,17 +89,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "reduceDomainVerified", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainVerifiedEventType), - org.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), org.DomainVerifiedEventMapper), + event: getEvent( + testEvent( + org.OrgDomainVerifiedEventType, + org.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), org.DomainVerifiedEventMapper), }, reduce: (&orgDomainProjection{}).reduceDomainVerified, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -121,17 +120,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "reducePrimaryDomainSet", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainPrimarySetEventType), - org.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), org.DomainPrimarySetEventMapper), + event: getEvent( + testEvent( + org.OrgDomainPrimarySetEventType, + org.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), org.DomainPrimarySetEventMapper), }, reduce: (&orgDomainProjection{}).reducePrimaryDomainSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -163,17 +162,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "reduceDomainRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainRemovedEventType), - org.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), org.DomainRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgDomainRemovedEventType, + org.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), org.DomainRemovedEventMapper), }, reduce: (&orgDomainProjection{}).reduceDomainRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -192,16 +191,16 @@ func TestOrgDomainProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&orgDomainProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -218,17 +217,17 @@ func TestOrgDomainProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(OrgDomainInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/org_member.go b/internal/query/projection/org_member.go index b606eee61..fd8ffc07e 100644 --- a/internal/query/projection/org_member.go +++ b/internal/query/projection/org_member.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -18,31 +18,47 @@ const ( ) type orgMemberProjection struct { - crdb.StatementHandler + es handler.EventStore } -func newOrgMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *orgMemberProjection { - p := new(orgMemberProjection) - config.ProjectionName = OrgMemberProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable( - append(memberColumns, crdb.NewColumn(OrgMemberOrgIDCol, crdb.ColumnTypeText)), - crdb.NewPrimaryKey(MemberInstanceID, OrgMemberOrgIDCol, MemberUserIDCol), - crdb.WithIndex(crdb.NewIndex("user_id", []string{MemberUserIDCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{MemberOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), +func newOrgMemberProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, &orgMemberProjection{es: config.Eventstore}) +} + +func (*orgMemberProjection) Name() string { + return OrgMemberProjectionTable +} + +func (*orgMemberProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable( + append(memberColumns, handler.NewColumn(OrgMemberOrgIDCol, handler.ColumnTypeText)), + handler.NewPrimaryKey(MemberInstanceID, OrgMemberOrgIDCol, MemberUserIDCol), + handler.WithIndex(handler.NewIndex("user_id", []string{MemberUserIDCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{MemberOwnerRemoved})), + handler.WithIndex(handler.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), + handler.WithIndex( + handler.NewIndex("om_instance", []string{MemberInstanceID}, + handler.WithInclude( + MemberCreationDate, + MemberChangeDate, + MemberUserOwnerRemoved, + MemberRolesCol, + MemberSequence, + MemberResourceOwner, + MemberOwnerRemoved, + ), + ), + ), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *orgMemberProjection) reducers() []handler.AggregateReducer { +func (p *orgMemberProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.MemberAddedEventType, Reduce: p.reduceAdded, @@ -67,7 +83,7 @@ func (p *orgMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, @@ -76,7 +92,7 @@ func (p *orgMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(MemberInstanceID), @@ -92,7 +108,7 @@ func (p *orgMemberProjection) reduceAdded(event eventstore.Event) (*handler.Stat return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-uYq4r", "reduce.wrong.event.type %s", org.MemberAddedEventType) } ctx := setMemberContext(e.Aggregate()) - userOwner, err := getResourceOwnerOfUser(ctx, p.Eventstore, e.Aggregate().InstanceID, e.UserID) + userOwner, err := getResourceOwnerOfUser(ctx, p.es, e.Aggregate().InstanceID, e.UserID) if err != nil { return nil, err } @@ -139,7 +155,7 @@ func (p *orgMemberProjection) reduceOrgRemoved(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-jnGAV", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, multiReduceMemberOwnerRemoved(e), multiReduceMemberUserOwnerRemoved(e), diff --git a/internal/query/projection/org_member_test.go b/internal/query/projection/org_member_test.go index b9edf34ba..dee53b5db 100644 --- a/internal/query/projection/org_member_test.go +++ b/internal/query/projection/org_member_test.go @@ -10,8 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -30,17 +29,18 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "org MemberAddedType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MemberAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MemberAddedEventType, + org.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role"] }`), - ), org.MemberAddedEventMapper), + ), org.MemberAddedEventMapper), }, reduce: (&orgMemberProjection{ - StatementHandler: getStatementHandlerWithFilters( + es: newMockEventStore().appendFilterResponse([]eventstore.Event{ user.NewHumanAddedEvent(context.Background(), &user.NewAggregate("user-id", "org1").Aggregate, "username1", @@ -53,11 +53,11 @@ func TestOrgMemberProjection_reduces(t *testing.T) { "email1", true, ), - )(t)}).reduceAdded, + }), + }).reduceAdded, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -66,7 +66,7 @@ func TestOrgMemberProjection_reduces(t *testing.T) { "user-id", "org1", false, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, anyArg{}, anyArg{}, uint64(15), @@ -83,26 +83,26 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "org MemberChangedType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MemberChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MemberChangedEventType, + org.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role", "changed"] }`), - ), org.MemberChangedEventMapper), + ), org.MemberChangedEventMapper), }, reduce: (&orgMemberProjection{}).reduceChanged, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.org_members3 SET (roles, change_date, sequence) = ($1, $2, $3) WHERE (instance_id = $4) AND (user_id = $5) AND (org_id = $6)", expectedArgs: []interface{}{ - database.StringArray{"role", "changed"}, + database.TextArray[string]{"role", "changed"}, anyArg{}, uint64(15), "instance-id", @@ -117,19 +117,19 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "org MemberCascadeRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MemberCascadeRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MemberCascadeRemovedEventType, + org.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), org.MemberCascadeRemovedEventMapper), + ), org.MemberCascadeRemovedEventMapper), }, reduce: (&orgMemberProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -147,19 +147,19 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "org MemberRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MemberRemovedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MemberRemovedEventType, + org.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), org.MemberRemovedEventMapper), + ), org.MemberRemovedEventMapper), }, reduce: (&orgMemberProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -177,17 +177,17 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "user UserRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&orgMemberProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -204,17 +204,17 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "org OrgRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - []byte(`{}`), - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + []byte(`{}`), + ), org.OrgRemovedEventMapper), }, reduce: (&orgMemberProjection{}).reduceOrgRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -238,17 +238,17 @@ func TestOrgMemberProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/org_metadata.go b/internal/query/projection/org_metadata.go index 3907bd4ee..e2e21f4e1 100644 --- a/internal/query/projection/org_metadata.go +++ b/internal/query/projection/org_metadata.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,40 +25,40 @@ const ( OrgMetadataColumnOwnerRemoved = "owner_removed" ) -type orgMetadataProjection struct { - crdb.StatementHandler +type orgMetadataProjection struct{} + +func (*orgMetadataProjection) Name() string { + return OrgMetadataProjectionTable } -func newOrgMetadataProjection(ctx context.Context, config crdb.StatementHandlerConfig) *orgMetadataProjection { - p := new(orgMetadataProjection) - config.ProjectionName = OrgMetadataProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(OrgMetadataColumnOrgID, crdb.ColumnTypeText), - crdb.NewColumn(OrgMetadataColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgMetadataColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(OrgMetadataColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(OrgMetadataColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(OrgMetadataColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(OrgMetadataColumnKey, crdb.ColumnTypeText), - crdb.NewColumn(OrgMetadataColumnValue, crdb.ColumnTypeBytes, crdb.Nullable()), - crdb.NewColumn(OrgMetadataColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*orgMetadataProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(OrgMetadataColumnOrgID, handler.ColumnTypeText), + handler.NewColumn(OrgMetadataColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgMetadataColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(OrgMetadataColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(OrgMetadataColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(OrgMetadataColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(OrgMetadataColumnKey, handler.ColumnTypeText), + handler.NewColumn(OrgMetadataColumnValue, handler.ColumnTypeBytes, handler.Nullable()), + handler.NewColumn(OrgMetadataColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(OrgMetadataColumnInstanceID, OrgMetadataColumnOrgID, OrgMetadataColumnKey), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{OrgMetadataColumnOwnerRemoved})), + handler.NewPrimaryKey(OrgMetadataColumnInstanceID, OrgMetadataColumnOrgID, OrgMetadataColumnKey), + handler.WithIndex(handler.NewIndex("owner_removed", []string{OrgMetadataColumnOwnerRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *orgMetadataProjection) reducers() []handler.AggregateReducer { +func newOrgMetadataProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(orgMetadataProjection)) +} + +func (p *orgMetadataProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.MetadataSetType, Reduce: p.reduceMetadataSet, @@ -79,7 +79,7 @@ func (p *orgMetadataProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(OrgMetadataColumnInstanceID), @@ -94,7 +94,7 @@ func (p *orgMetadataProjection) reduceMetadataSet(event eventstore.Event) (*hand if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Ghn53", "reduce.wrong.event.type %s", org.MetadataSetType) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( e, []handler.Column{ handler.NewCol(OrgMetadataColumnInstanceID, nil), @@ -119,7 +119,7 @@ func (p *orgMetadataProjection) reduceMetadataRemoved(event eventstore.Event) (* if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Bm542", "reduce.wrong.event.type %s", org.MetadataRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(OrgMetadataColumnOrgID, e.Aggregate().ID), @@ -137,7 +137,7 @@ func (p *orgMetadataProjection) reduceMetadataRemovedAll(event eventstore.Event) default: return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Bmnf3", "reduce.wrong.event.type %v", []eventstore.EventType{org.MetadataRemovedAllType, org.OrgRemovedEventType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(OrgMetadataColumnOrgID, event.Aggregate().ID), @@ -152,7 +152,7 @@ func (p *orgMetadataProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Hkd1f", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(OrgMetadataColumnChangeDate, e.CreationDate()), diff --git a/internal/query/projection/org_metadata_test.go b/internal/query/projection/org_metadata_test.go index 034dbddd0..b0fce9911 100644 --- a/internal/query/projection/org_metadata_test.go +++ b/internal/query/projection/org_metadata_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -24,20 +23,20 @@ func TestOrgMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataSet", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MetadataSetType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MetadataSetType, + org.AggregateType, + []byte(`{ "key": "key", "value": "dmFsdWU=" }`), - ), org.MetadataSetEventMapper), + ), org.MetadataSetEventMapper), }, reduce: (&orgMetadataProjection{}).reduceMetadataSet, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -60,19 +59,19 @@ func TestOrgMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MetadataRemovedType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.MetadataRemovedType, + org.AggregateType, + []byte(`{ "key": "key" }`), - ), org.MetadataRemovedEventMapper), + ), org.MetadataRemovedEventMapper), }, reduce: (&orgMetadataProjection{}).reduceMetadataRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -90,17 +89,17 @@ func TestOrgMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataRemovedAll", args: args{ - event: getEvent(testEvent( - repository.EventType(org.MetadataRemovedAllType), - org.AggregateType, - nil, - ), org.MetadataRemovedAllEventMapper), + event: getEvent( + testEvent( + org.MetadataRemovedAllType, + org.AggregateType, + nil, + ), org.MetadataRemovedAllEventMapper), }, reduce: (&orgMetadataProjection{}).reduceMetadataRemovedAll, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -117,17 +116,17 @@ func TestOrgMetadataProjection_reduces(t *testing.T) { { name: "reduceOwnerRemoved(org removed)", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, reduce: (&orgMetadataProjection{}).reduceOwnerRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -147,17 +146,17 @@ func TestOrgMetadataProjection_reduces(t *testing.T) { { name: "reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/org_test.go b/internal/query/projection/org_test.go index a5f6795fb..94d72469a 100644 --- a/internal/query/projection/org_test.go +++ b/internal/query/projection/org_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,17 +24,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reducePrimaryDomainSet", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDomainPrimarySetEventType), - org.AggregateType, - []byte(`{"domain": "domain.new"}`), - ), org.DomainPrimarySetEventMapper), + event: getEvent( + testEvent( + org.OrgDomainPrimarySetEventType, + org.AggregateType, + []byte(`{"domain": "domain.new"}`), + ), org.DomainPrimarySetEventMapper), }, reduce: (&orgProjection{}).reducePrimaryDomainSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -55,17 +54,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reduceOrgReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgReactivatedEventType), - org.AggregateType, - nil, - ), org.OrgReactivatedEventMapper), + event: getEvent( + testEvent( + org.OrgReactivatedEventType, + org.AggregateType, + nil, + ), org.OrgReactivatedEventMapper), }, reduce: (&orgProjection{}).reduceOrgReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -85,17 +84,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reduceOrgDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgDeactivatedEventType), - org.AggregateType, - nil, - ), org.OrgDeactivatedEventMapper), + event: getEvent( + testEvent( + org.OrgDeactivatedEventType, + org.AggregateType, + nil, + ), org.OrgDeactivatedEventMapper), }, reduce: (&orgProjection{}).reduceOrgDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -115,17 +114,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reduceOrgChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgChangedEventType), - org.AggregateType, - []byte(`{"name": "new name"}`), - ), org.OrgChangedEventMapper), + event: getEvent( + testEvent( + org.OrgChangedEventType, + org.AggregateType, + []byte(`{"name": "new name"}`), + ), org.OrgChangedEventMapper), }, reduce: (&orgProjection{}).reduceOrgChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -145,34 +144,34 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reduceOrgChanged no changes", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgChangedEventType), - org.AggregateType, - []byte(`{}`), - ), org.OrgChangedEventMapper), + event: getEvent( + testEvent( + org.OrgChangedEventType, + org.AggregateType, + []byte(`{}`), + ), org.OrgChangedEventMapper), }, reduce: (&orgProjection{}).reduceOrgChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "reduceOrgAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgAddedEventType), - org.AggregateType, - []byte(`{"name": "name"}`), - ), org.OrgAddedEventMapper), + event: getEvent( + testEvent( + org.OrgAddedEventType, + org.AggregateType, + []byte(`{"name": "name"}`), + ), org.OrgAddedEventMapper), }, reduce: (&orgProjection{}).reduceOrgAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -195,17 +194,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "reduceOrgRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, reduce: (&orgProjection{}).reduceOrgRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -222,17 +221,17 @@ func TestOrgProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(OrgColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/password_age_policy.go b/internal/query/projection/password_age_policy.go index f03ee0d55..03e0aee33 100644 --- a/internal/query/projection/password_age_policy.go +++ b/internal/query/projection/password_age_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -29,41 +29,42 @@ const ( AgePolicyOwnerRemovedCol = "owner_removed" ) -type passwordAgeProjection struct { - crdb.StatementHandler +type passwordAgeProjection struct{} + +func newPasswordAgeProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(passwordAgeProjection)) } -func newPasswordAgeProjection(ctx context.Context, config crdb.StatementHandlerConfig) *passwordAgeProjection { - p := new(passwordAgeProjection) - config.ProjectionName = PasswordAgeTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(AgePolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AgePolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AgePolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(AgePolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(AgePolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(AgePolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(AgePolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(AgePolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(AgePolicyExpireWarnDaysCol, crdb.ColumnTypeInt64), - crdb.NewColumn(AgePolicyMaxAgeDaysCol, crdb.ColumnTypeInt64), - crdb.NewColumn(AgePolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*passwordAgeProjection) Name() string { + return PasswordAgeTable +} + +func (*passwordAgeProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(AgePolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(AgePolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(AgePolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(AgePolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(AgePolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(AgePolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(AgePolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(AgePolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(AgePolicyExpireWarnDaysCol, handler.ColumnTypeInt64), + handler.NewColumn(AgePolicyMaxAgeDaysCol, handler.ColumnTypeInt64), + handler.NewColumn(AgePolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(AgePolicyInstanceIDCol, AgePolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{AgePolicyOwnerRemovedCol})), + handler.NewPrimaryKey(AgePolicyInstanceIDCol, AgePolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{AgePolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *passwordAgeProjection) reducers() []handler.AggregateReducer { +func (p *passwordAgeProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.PasswordAgePolicyAddedEventType, Reduce: p.reduceAdded, @@ -84,7 +85,7 @@ func (p *passwordAgeProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.PasswordAgePolicyAddedEventType, Reduce: p.reduceAdded, @@ -115,7 +116,7 @@ func (p *passwordAgeProjection) reduceAdded(event eventstore.Event) (*handler.St default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-CJqF0", "reduce.wrong.event.type %v", []eventstore.EventType{org.PasswordAgePolicyAddedEventType, instance.PasswordAgePolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(AgePolicyCreationDateCol, policyEvent.CreationDate()), @@ -151,7 +152,7 @@ func (p *passwordAgeProjection) reduceChanged(event eventstore.Event) (*handler. if policyEvent.MaxAgeDays != nil { cols = append(cols, handler.NewCol(AgePolicyMaxAgeDaysCol, *policyEvent.MaxAgeDays)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -165,7 +166,7 @@ func (p *passwordAgeProjection) reduceRemoved(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-EtHWB", "reduce.wrong.event.type %s", org.PasswordAgePolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(AgePolicyIDCol, policyEvent.Aggregate().ID), @@ -179,7 +180,7 @@ func (p *passwordAgeProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-edLs2", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(AgePolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/password_age_policy_test.go b/internal/query/projection/password_age_policy_test.go index f6c5e801f..8ce044e78 100644 --- a/internal/query/projection/password_age_policy_test.go +++ b/internal/query/projection/password_age_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,20 +24,20 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordAgePolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PasswordAgePolicyAddedEventType, + org.AggregateType, + []byte(`{ "expireWarnDays": 10, "maxAgeDays": 13 }`), - ), org.PasswordAgePolicyAddedEventMapper), + ), org.PasswordAgePolicyAddedEventMapper), }, reduce: (&passwordAgeProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -64,19 +63,19 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (&passwordAgeProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordAgePolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PasswordAgePolicyChangedEventType, + org.AggregateType, + []byte(`{ "expireWarnDays": 10, "maxAgeDays": 13 }`), - ), org.PasswordAgePolicyChangedEventMapper), + ), org.PasswordAgePolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -98,16 +97,16 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&passwordAgeProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordAgePolicyRemovedEventType), - org.AggregateType, - nil, - ), org.PasswordAgePolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.PasswordAgePolicyRemovedEventType, + org.AggregateType, + nil, + ), org.PasswordAgePolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -124,17 +123,17 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(AgePolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -151,19 +150,19 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&passwordAgeProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PasswordAgePolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PasswordAgePolicyAddedEventType, + instance.AggregateType, + []byte(`{ "expireWarnDays": 10, "maxAgeDays": 13 }`), - ), instance.PasswordAgePolicyAddedEventMapper), + ), instance.PasswordAgePolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -189,19 +188,19 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (&passwordAgeProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PasswordAgePolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PasswordAgePolicyChangedEventType, + instance.AggregateType, + []byte(`{ "expireWarnDays": 10, "maxAgeDays": 13 }`), - ), instance.PasswordAgePolicyChangedEventMapper), + ), instance.PasswordAgePolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -223,16 +222,16 @@ func TestPasswordAgeProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&passwordAgeProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/password_complexity_policy.go b/internal/query/projection/password_complexity_policy.go index b683cd556..96072017d 100644 --- a/internal/query/projection/password_complexity_policy.go +++ b/internal/query/projection/password_complexity_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -32,44 +32,45 @@ const ( ComplexityPolicyOwnerRemovedCol = "owner_removed" ) -type passwordComplexityProjection struct { - crdb.StatementHandler +type passwordComplexityProjection struct{} + +func newPasswordComplexityProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(passwordComplexityProjection)) } -func newPasswordComplexityProjection(ctx context.Context, config crdb.StatementHandlerConfig) *passwordComplexityProjection { - p := new(passwordComplexityProjection) - config.ProjectionName = PasswordComplexityTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(ComplexityPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ComplexityPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ComplexityPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ComplexityPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(ComplexityPolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(ComplexityPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(ComplexityPolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(ComplexityPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ComplexityPolicyMinLengthCol, crdb.ColumnTypeInt64), - crdb.NewColumn(ComplexityPolicyHasLowercaseCol, crdb.ColumnTypeBool), - crdb.NewColumn(ComplexityPolicyHasUppercaseCol, crdb.ColumnTypeBool), - crdb.NewColumn(ComplexityPolicyHasSymbolCol, crdb.ColumnTypeBool), - crdb.NewColumn(ComplexityPolicyHasNumberCol, crdb.ColumnTypeBool), - crdb.NewColumn(ComplexityPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*passwordComplexityProjection) Name() string { + return PasswordComplexityTable +} + +func (*passwordComplexityProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(ComplexityPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(ComplexityPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(ComplexityPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(ComplexityPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(ComplexityPolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(ComplexityPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(ComplexityPolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(ComplexityPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(ComplexityPolicyMinLengthCol, handler.ColumnTypeInt64), + handler.NewColumn(ComplexityPolicyHasLowercaseCol, handler.ColumnTypeBool), + handler.NewColumn(ComplexityPolicyHasUppercaseCol, handler.ColumnTypeBool), + handler.NewColumn(ComplexityPolicyHasSymbolCol, handler.ColumnTypeBool), + handler.NewColumn(ComplexityPolicyHasNumberCol, handler.ColumnTypeBool), + handler.NewColumn(ComplexityPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(ComplexityPolicyInstanceIDCol, ComplexityPolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{ComplexityPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(ComplexityPolicyInstanceIDCol, ComplexityPolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{ComplexityPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *passwordComplexityProjection) reducers() []handler.AggregateReducer { +func (p *passwordComplexityProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.PasswordComplexityPolicyAddedEventType, Reduce: p.reduceAdded, @@ -90,7 +91,7 @@ func (p *passwordComplexityProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.PasswordComplexityPolicyAddedEventType, Reduce: p.reduceAdded, @@ -121,7 +122,7 @@ func (p *passwordComplexityProjection) reduceAdded(event eventstore.Event) (*han default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-KTHmJ", "reduce.wrong.event.type %v", []eventstore.EventType{org.PasswordComplexityPolicyAddedEventType, instance.PasswordComplexityPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(ComplexityPolicyCreationDateCol, policyEvent.CreationDate()), @@ -169,7 +170,7 @@ func (p *passwordComplexityProjection) reduceChanged(event eventstore.Event) (*h if policyEvent.HasNumber != nil { cols = append(cols, handler.NewCol(ComplexityPolicyHasNumberCol, *policyEvent.HasNumber)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -183,7 +184,7 @@ func (p *passwordComplexityProjection) reduceRemoved(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-wttCd", "reduce.wrong.event.type %s", org.PasswordComplexityPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(ComplexityPolicyIDCol, policyEvent.Aggregate().ID), @@ -197,7 +198,7 @@ func (p *passwordComplexityProjection) reduceOwnerRemoved(event eventstore.Event return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-pGTz9", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ComplexityPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/password_complexity_policy_test.go b/internal/query/projection/password_complexity_policy_test.go index ceac96470..0428d1ae2 100644 --- a/internal/query/projection/password_complexity_policy_test.go +++ b/internal/query/projection/password_complexity_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,23 +24,23 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordComplexityPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PasswordComplexityPolicyAddedEventType, + org.AggregateType, + []byte(`{ "minLength": 10, "hasLowercase": true, "hasUppercase": true, "HasNumber": true, "HasSymbol": true }`), - ), org.PasswordComplexityPolicyAddedEventMapper), + ), org.PasswordComplexityPolicyAddedEventMapper), }, reduce: (&passwordComplexityProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -70,22 +69,22 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (&passwordComplexityProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordComplexityPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PasswordComplexityPolicyChangedEventType, + org.AggregateType, + []byte(`{ "minLength": 11, "hasLowercase": true, "hasUppercase": true, "HasNumber": true, "HasSymbol": true }`), - ), org.PasswordComplexityPolicyChangedEventMapper), + ), org.PasswordComplexityPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -110,16 +109,16 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&passwordComplexityProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PasswordComplexityPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.PasswordComplexityPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.PasswordComplexityPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.PasswordComplexityPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -136,17 +135,17 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(ComplexityPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -163,22 +162,22 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&passwordComplexityProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PasswordComplexityPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PasswordComplexityPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "minLength": 10, "hasLowercase": true, "hasUppercase": true, "HasNumber": true, "HasSymbol": true }`), - ), instance.PasswordComplexityPolicyAddedEventMapper), + ), instance.PasswordComplexityPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -207,22 +206,22 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (&passwordComplexityProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PasswordComplexityPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PasswordComplexityPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "minLength": 10, "hasLowercase": true, "hasUppercase": true, "HasNumber": true, "HasSymbol": true }`), - ), instance.PasswordComplexityPolicyChangedEventMapper), + ), instance.PasswordComplexityPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -247,16 +246,16 @@ func TestPasswordComplexityProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&passwordComplexityProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/privacy_policy.go b/internal/query/projection/privacy_policy.go index b577b5eff..44ac5794f 100644 --- a/internal/query/projection/privacy_policy.go +++ b/internal/query/projection/privacy_policy.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/policy" @@ -31,43 +31,44 @@ const ( PrivacyPolicyOwnerRemovedCol = "owner_removed" ) -type privacyPolicyProjection struct { - crdb.StatementHandler +type privacyPolicyProjection struct{} + +func newPrivacyPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(privacyPolicyProjection)) } -func newPrivacyPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *privacyPolicyProjection { - p := new(privacyPolicyProjection) - config.ProjectionName = PrivacyPolicyTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(PrivacyPolicyIDCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(PrivacyPolicyChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(PrivacyPolicySequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(PrivacyPolicyStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(PrivacyPolicyIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(PrivacyPolicyResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyPrivacyLinkCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyTOSLinkCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyHelpLinkCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicySupportEmailCol, crdb.ColumnTypeText), - crdb.NewColumn(PrivacyPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*privacyPolicyProjection) Name() string { + return PrivacyPolicyTable +} + +func (*privacyPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(PrivacyPolicyIDCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(PrivacyPolicyChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(PrivacyPolicySequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(PrivacyPolicyStateCol, handler.ColumnTypeEnum), + handler.NewColumn(PrivacyPolicyIsDefaultCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(PrivacyPolicyResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyPrivacyLinkCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyTOSLinkCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyHelpLinkCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicySupportEmailCol, handler.ColumnTypeText), + handler.NewColumn(PrivacyPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(PrivacyPolicyInstanceIDCol, PrivacyPolicyIDCol), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{PrivacyPolicyOwnerRemovedCol})), + handler.NewPrimaryKey(PrivacyPolicyInstanceIDCol, PrivacyPolicyIDCol), + handler.WithIndex(handler.NewIndex("owner_removed", []string{PrivacyPolicyOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *privacyPolicyProjection) reducers() []handler.AggregateReducer { +func (p *privacyPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.PrivacyPolicyAddedEventType, Reduce: p.reduceAdded, @@ -88,7 +89,7 @@ func (p *privacyPolicyProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.PrivacyPolicyAddedEventType, Reduce: p.reduceAdded, @@ -119,7 +120,7 @@ func (p *privacyPolicyProjection) reduceAdded(event eventstore.Event) (*handler. default: return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-kRNh8", "reduce.wrong.event.type %v", []eventstore.EventType{org.PrivacyPolicyAddedEventType, instance.PrivacyPolicyAddedEventType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( &policyEvent, []handler.Column{ handler.NewCol(PrivacyPolicyCreationDateCol, policyEvent.CreationDate()), @@ -163,7 +164,7 @@ func (p *privacyPolicyProjection) reduceChanged(event eventstore.Event) (*handle if policyEvent.SupportEmail != nil { cols = append(cols, handler.NewCol(PrivacyPolicySupportEmailCol, *policyEvent.SupportEmail)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( &policyEvent, cols, []handler.Condition{ @@ -177,7 +178,7 @@ func (p *privacyPolicyProjection) reduceRemoved(event eventstore.Event) (*handle if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-FvtGO", "reduce.wrong.event.type %s", org.PrivacyPolicyRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( policyEvent, []handler.Condition{ handler.NewCond(PrivacyPolicyIDCol, policyEvent.Aggregate().ID), @@ -191,7 +192,7 @@ func (p *privacyPolicyProjection) reduceOwnerRemoved(event eventstore.Event) (*h return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-bxJCY", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(PrivacyPolicyInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/privacy_policy_test.go b/internal/query/projection/privacy_policy_test.go index 630abc6f1..53c147507 100644 --- a/internal/query/projection/privacy_policy_test.go +++ b/internal/query/projection/privacy_policy_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) @@ -25,21 +24,21 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { { name: "org reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(org.PrivacyPolicyAddedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PrivacyPolicyAddedEventType, + org.AggregateType, + []byte(`{ "tosLink": "http://tos.link", "privacyLink": "http://privacy.link", "helpLink": "http://help.link", "supportEmail": "support@example.com"}`), - ), org.PrivacyPolicyAddedEventMapper), + ), org.PrivacyPolicyAddedEventMapper), }, reduce: (&privacyPolicyProjection{}).reduceAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -67,20 +66,20 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { name: "org reduceChanged", reduce: (&privacyPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PrivacyPolicyChangedEventType), - org.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + org.PrivacyPolicyChangedEventType, + org.AggregateType, + []byte(`{ "tosLink": "http://tos.link", "privacyLink": "http://privacy.link", "helpLink": "http://help.link", "supportEmail": "support@example.com"}`), - ), org.PrivacyPolicyChangedEventMapper), + ), org.PrivacyPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -104,16 +103,16 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { name: "org reduceRemoved", reduce: (&privacyPolicyProjection{}).reduceRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.PrivacyPolicyRemovedEventType), - org.AggregateType, - nil, - ), org.PrivacyPolicyRemovedEventMapper), + event: getEvent( + testEvent( + org.PrivacyPolicyRemovedEventType, + org.AggregateType, + nil, + ), org.PrivacyPolicyRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -129,17 +128,17 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { }, { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(PrivacyPolicyInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -156,20 +155,20 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { name: "instance reduceAdded", reduce: (&privacyPolicyProjection{}).reduceAdded, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PrivacyPolicyAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PrivacyPolicyAddedEventType, + instance.AggregateType, + []byte(`{ "tosLink": "http://tos.link", "privacyLink": "http://privacy.link", "helpLink": "http://help.link", "supportEmail": "support@example.com"}`), - ), instance.PrivacyPolicyAddedEventMapper), + ), instance.PrivacyPolicyAddedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -197,20 +196,20 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { name: "instance reduceChanged", reduce: (&privacyPolicyProjection{}).reduceChanged, args: args{ - event: getEvent(testEvent( - repository.EventType(instance.PrivacyPolicyChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.PrivacyPolicyChangedEventType, + instance.AggregateType, + []byte(`{ "tosLink": "http://tos.link", "privacyLink": "http://privacy.link", "helpLink": "http://help.link", "supportEmail": "support@example.com"}`), - ), instance.PrivacyPolicyChangedEventMapper), + ), instance.PrivacyPolicyChangedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -234,16 +233,16 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&privacyPolicyProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/project.go b/internal/query/projection/project.go index 0848cad6a..c02e16f6d 100644 --- a/internal/query/projection/project.go +++ b/internal/query/projection/project.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -31,44 +31,45 @@ const ( ProjectColumnOwnerRemoved = "owner_removed" ) -type projectProjection struct { - crdb.StatementHandler +type projectProjection struct{} + +func newProjectProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(projectProjection)) } -func newProjectProjection(ctx context.Context, config crdb.StatementHandlerConfig) *projectProjection { - p := new(projectProjection) - config.ProjectionName = ProjectProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(ProjectColumnID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(ProjectColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(ProjectColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(ProjectColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectColumnName, crdb.ColumnTypeText), - crdb.NewColumn(ProjectColumnProjectRoleAssertion, crdb.ColumnTypeBool), - crdb.NewColumn(ProjectColumnProjectRoleCheck, crdb.ColumnTypeBool), - crdb.NewColumn(ProjectColumnHasProjectCheck, crdb.ColumnTypeBool), - crdb.NewColumn(ProjectColumnPrivateLabelingSetting, crdb.ColumnTypeEnum), - crdb.NewColumn(ProjectColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*projectProjection) Name() string { + return ProjectProjectionTable +} + +func (*projectProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(ProjectColumnID, handler.ColumnTypeText), + handler.NewColumn(ProjectColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(ProjectColumnState, handler.ColumnTypeEnum), + handler.NewColumn(ProjectColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(ProjectColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(ProjectColumnName, handler.ColumnTypeText), + handler.NewColumn(ProjectColumnProjectRoleAssertion, handler.ColumnTypeBool), + handler.NewColumn(ProjectColumnProjectRoleCheck, handler.ColumnTypeBool), + handler.NewColumn(ProjectColumnHasProjectCheck, handler.ColumnTypeBool), + handler.NewColumn(ProjectColumnPrivateLabelingSetting, handler.ColumnTypeEnum), + handler.NewColumn(ProjectColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(ProjectColumnInstanceID, ProjectColumnID), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{ProjectColumnResourceOwner})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{ProjectColumnOwnerRemoved})), + handler.NewPrimaryKey(ProjectColumnInstanceID, ProjectColumnID), + handler.WithIndex(handler.NewIndex("resource_owner", []string{ProjectColumnResourceOwner})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{ProjectColumnOwnerRemoved})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *projectProjection) reducers() []handler.AggregateReducer { +func (p *projectProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.ProjectAddedType, Reduce: p.reduceProjectAdded, @@ -93,7 +94,7 @@ func (p *projectProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -102,7 +103,7 @@ func (p *projectProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(ProjectColumnInstanceID), @@ -117,7 +118,7 @@ func (p *projectProjection) reduceProjectAdded(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-l000S", "reduce.wrong.event.type %s", project.ProjectAddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(ProjectColumnID, e.Aggregate().ID), @@ -142,7 +143,7 @@ func (p *projectProjection) reduceProjectChanged(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-s00Fs", "reduce.wrong.event.type %s", project.ProjectChangedType) } if e.Name == nil && e.HasProjectCheck == nil && e.ProjectRoleAssertion == nil && e.ProjectRoleCheck == nil && e.PrivateLabelingSetting == nil { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } columns := make([]handler.Column, 0, 7) @@ -163,7 +164,7 @@ func (p *projectProjection) reduceProjectChanged(event eventstore.Event) (*handl if e.PrivateLabelingSetting != nil { columns = append(columns, handler.NewCol(ProjectColumnPrivateLabelingSetting, *e.PrivateLabelingSetting)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, columns, []handler.Condition{ @@ -178,7 +179,7 @@ func (p *projectProjection) reduceProjectDeactivated(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-LLp0f", "reduce.wrong.event.type %s", project.ProjectDeactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectColumnChangeDate, e.CreationDate()), @@ -197,7 +198,7 @@ func (p *projectProjection) reduceProjectReactivated(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-9J98f", "reduce.wrong.event.type %s", project.ProjectReactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectColumnChangeDate, e.CreationDate()), @@ -216,7 +217,7 @@ func (p *projectProjection) reduceProjectRemoved(event eventstore.Event) (*handl if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-5N9fs", "reduce.wrong.event.type %s", project.ProjectRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectColumnID, e.Aggregate().ID), @@ -231,7 +232,7 @@ func (p *projectProjection) reduceOwnerRemoved(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-sbgru", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectColumnInstanceID, e.Aggregate().InstanceID), diff --git a/internal/query/projection/project_grant.go b/internal/query/projection/project_grant.go index bb90298a1..7dbe17cd6 100644 --- a/internal/query/projection/project_grant.go +++ b/internal/query/projection/project_grant.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -31,45 +31,46 @@ const ( ProjectGrantColumnGrantedOrgRemoved = "granted_org_removed" ) -type projectGrantProjection struct { - crdb.StatementHandler +type projectGrantProjection struct{} + +func newProjectGrantProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(projectGrantProjection)) } -func newProjectGrantProjection(ctx context.Context, config crdb.StatementHandlerConfig) *projectGrantProjection { - p := new(projectGrantProjection) - config.ProjectionName = ProjectGrantProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(ProjectGrantColumnGrantID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectGrantColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectGrantColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(ProjectGrantColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(ProjectGrantColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantColumnProjectID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantColumnGrantedOrgID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantColumnRoleKeys, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(ProjectGrantColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(ProjectGrantColumnGrantedOrgRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*projectGrantProjection) Name() string { + return ProjectGrantProjectionTable +} + +func (*projectGrantProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(ProjectGrantColumnGrantID, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectGrantColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectGrantColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(ProjectGrantColumnState, handler.ColumnTypeEnum), + handler.NewColumn(ProjectGrantColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantColumnProjectID, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantColumnGrantedOrgID, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantColumnRoleKeys, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(ProjectGrantColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(ProjectGrantColumnGrantedOrgRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(ProjectGrantColumnInstanceID, ProjectGrantColumnGrantID), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{ProjectGrantColumnResourceOwner})), - crdb.WithIndex(crdb.NewIndex("granted_org", []string{ProjectGrantColumnGrantedOrgID})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{ProjectGrantColumnOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("granted_org_removed", []string{ProjectGrantColumnGrantedOrgRemoved})), + handler.NewPrimaryKey(ProjectGrantColumnInstanceID, ProjectGrantColumnGrantID), + handler.WithIndex(handler.NewIndex("resource_owner", []string{ProjectGrantColumnResourceOwner})), + handler.WithIndex(handler.NewIndex("granted_org", []string{ProjectGrantColumnGrantedOrgID})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{ProjectGrantColumnOwnerRemoved})), + handler.WithIndex(handler.NewIndex("granted_org_removed", []string{ProjectGrantColumnGrantedOrgRemoved})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *projectGrantProjection) reducers() []handler.AggregateReducer { +func (p *projectGrantProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.GrantAddedType, Reduce: p.reduceProjectGrantAdded, @@ -102,7 +103,7 @@ func (p *projectGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -111,7 +112,7 @@ func (p *projectGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(ProjectGrantColumnInstanceID), @@ -126,7 +127,7 @@ func (p *projectGrantProjection) reduceProjectGrantAdded(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-g92Fg", "reduce.wrong.event.type %s", project.GrantAddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(ProjectGrantColumnGrantID, e.GrantID), @@ -138,7 +139,7 @@ func (p *projectGrantProjection) reduceProjectGrantAdded(event eventstore.Event) handler.NewCol(ProjectGrantColumnState, domain.ProjectGrantStateActive), handler.NewCol(ProjectGrantColumnSequence, e.Sequence()), handler.NewCol(ProjectGrantColumnGrantedOrgID, e.GrantedOrgID), - handler.NewCol(ProjectGrantColumnRoleKeys, database.StringArray(e.RoleKeys)), + handler.NewCol(ProjectGrantColumnRoleKeys, database.TextArray[string](e.RoleKeys)), }, ), nil } @@ -148,12 +149,12 @@ func (p *projectGrantProjection) reduceProjectGrantChanged(event eventstore.Even if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-g0fg4", "reduce.wrong.event.type %s", project.GrantChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectColumnChangeDate, e.CreationDate()), handler.NewCol(ProjectGrantColumnSequence, e.Sequence()), - handler.NewCol(ProjectGrantColumnRoleKeys, database.StringArray(e.RoleKeys)), + handler.NewCol(ProjectGrantColumnRoleKeys, database.TextArray[string](e.RoleKeys)), }, []handler.Condition{ handler.NewCond(ProjectGrantColumnGrantID, e.GrantID), @@ -168,12 +169,12 @@ func (p *projectGrantProjection) reduceProjectGrantCascadeChanged(event eventsto if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ll9Ts", "reduce.wrong.event.type %s", project.GrantCascadeChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectGrantColumnChangeDate, e.CreationDate()), handler.NewCol(ProjectGrantColumnSequence, e.Sequence()), - handler.NewCol(ProjectGrantColumnRoleKeys, database.StringArray(e.RoleKeys)), + handler.NewCol(ProjectGrantColumnRoleKeys, database.TextArray[string](e.RoleKeys)), }, []handler.Condition{ handler.NewCond(ProjectGrantColumnGrantID, e.GrantID), @@ -188,7 +189,7 @@ func (p *projectGrantProjection) reduceProjectGrantDeactivated(event eventstore. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-0fj2f", "reduce.wrong.event.type %s", project.GrantDeactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectGrantColumnChangeDate, e.CreationDate()), @@ -208,7 +209,7 @@ func (p *projectGrantProjection) reduceProjectGrantReactivated(event eventstore. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2M0ve", "reduce.wrong.event.type %s", project.GrantReactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(ProjectGrantColumnChangeDate, e.CreationDate()), @@ -228,7 +229,7 @@ func (p *projectGrantProjection) reduceProjectGrantRemoved(event eventstore.Even if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-o0w4f", "reduce.wrong.event.type %s", project.GrantRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectGrantColumnGrantID, e.GrantID), @@ -243,7 +244,7 @@ func (p *projectGrantProjection) reduceProjectRemoved(event eventstore.Event) (* if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-gn9rw", "reduce.wrong.event.type %s", project.ProjectRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectGrantColumnProjectID, e.Aggregate().ID), @@ -258,15 +259,15 @@ func (p *projectGrantProjection) reduceOwnerRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-HDgW3", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(ProjectGrantColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(ProjectGrantColumnResourceOwner, e.Aggregate().ID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(ProjectGrantColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(ProjectGrantColumnGrantedOrgID, e.Aggregate().ID), diff --git a/internal/query/projection/project_grant_member.go b/internal/query/projection/project_grant_member.go index 596db4143..934ed5076 100644 --- a/internal/query/projection/project_grant_member.go +++ b/internal/query/projection/project_grant_member.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/org" @@ -23,38 +23,54 @@ const ( ) type projectGrantMemberProjection struct { - crdb.StatementHandler + es handler.EventStore } -func newProjectGrantMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *projectGrantMemberProjection { - p := new(projectGrantMemberProjection) - config.ProjectionName = ProjectGrantMemberProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable( +func newProjectGrantMemberProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, &projectGrantMemberProjection{es: config.Eventstore}) +} + +func (*projectGrantMemberProjection) Name() string { + return ProjectGrantMemberProjectionTable +} + +func (*projectGrantMemberProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable( append(memberColumns, - crdb.NewColumn(ProjectGrantMemberProjectIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantMemberGrantIDCol, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantMemberGrantedOrg, crdb.ColumnTypeText), - crdb.NewColumn(ProjectGrantMemberGrantedOrgRemoved, crdb.ColumnTypeBool, crdb.Default(false)), + handler.NewColumn(ProjectGrantMemberProjectIDCol, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantMemberGrantIDCol, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantMemberGrantedOrg, handler.ColumnTypeText), + handler.NewColumn(ProjectGrantMemberGrantedOrgRemoved, handler.ColumnTypeBool, handler.Default(false)), + ), + handler.NewPrimaryKey(MemberInstanceID, ProjectGrantMemberProjectIDCol, ProjectGrantMemberGrantIDCol, MemberUserIDCol), + handler.WithIndex(handler.NewIndex("user_id", []string{MemberUserIDCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{MemberOwnerRemoved})), + handler.WithIndex(handler.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), + handler.WithIndex(handler.NewIndex("granted_org_removed", []string{ProjectGrantMemberGrantedOrgRemoved})), + handler.WithIndex( + handler.NewIndex("pgm_instance", []string{MemberInstanceID}, + handler.WithInclude( + MemberCreationDate, + MemberChangeDate, + MemberUserOwnerRemoved, + MemberRolesCol, + MemberSequence, + MemberResourceOwner, + MemberOwnerRemoved, + ProjectGrantMemberGrantedOrgRemoved, + ), + ), ), - crdb.NewPrimaryKey(MemberInstanceID, ProjectGrantMemberProjectIDCol, ProjectGrantMemberGrantIDCol, MemberUserIDCol), - crdb.WithIndex(crdb.NewIndex("user_id", []string{MemberUserIDCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{MemberOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("granted_org_removed", []string{ProjectGrantMemberGrantedOrgRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *projectGrantMemberProjection) reducers() []handler.AggregateReducer { +func (p *projectGrantMemberProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.GrantMemberAddedType, Reduce: p.reduceAdded, @@ -83,7 +99,7 @@ func (p *projectGrantMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, @@ -92,7 +108,7 @@ func (p *projectGrantMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOrgRemoved, @@ -101,7 +117,7 @@ func (p *projectGrantMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(MemberInstanceID), @@ -117,11 +133,11 @@ func (p *projectGrantMemberProjection) reduceAdded(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-0EBQf", "reduce.wrong.event.type %s", project.GrantMemberAddedType) } ctx := setMemberContext(e.Aggregate()) - userOwner, err := getResourceOwnerOfUser(ctx, p.Eventstore, e.Aggregate().InstanceID, e.UserID) + userOwner, err := getResourceOwnerOfUser(ctx, p.es, e.Aggregate().InstanceID, e.UserID) if err != nil { return nil, err } - grantedOrg, err := getGrantedOrgOfGrantedProject(ctx, p.Eventstore, e.Aggregate().InstanceID, e.Aggregate().ID, e.GrantID) + grantedOrg, err := getGrantedOrgOfGrantedProject(ctx, p.es, e.Aggregate().InstanceID, e.Aggregate().ID, e.GrantID) if err != nil { return nil, err } @@ -192,11 +208,11 @@ func (p *projectGrantMemberProjection) reduceOrgRemoved(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Zzp6o", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, multiReduceMemberOwnerRemoved(e), multiReduceMemberUserOwnerRemoved(e), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(ProjectGrantColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(ProjectGrantMemberGrantedOrg, e.Aggregate().ID), diff --git a/internal/query/projection/project_grant_member_test.go b/internal/query/projection/project_grant_member_test.go index 12a032b9b..0f9ae2260 100644 --- a/internal/query/projection/project_grant_member_test.go +++ b/internal/query/projection/project_grant_member_test.go @@ -10,8 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -31,39 +30,45 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project GrantMemberAddedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantMemberAddedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.GrantMemberAddedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role"], "grantId": "grant-id" }`), - ), project.GrantMemberAddedEventMapper), + ), project.GrantMemberAddedEventMapper), }, reduce: (&projectGrantMemberProjection{ - StatementHandler: getStatementHandlerWithFilters( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user-id", "org1").Aggregate, - "username1", - "firstname1", - "lastname1", - "nickname1", - "displayname1", - language.German, - domain.GenderMale, - "email1", - true, - ), - project.NewGrantAddedEvent(context.Background(), - &project.NewAggregate("project1", "org2").Aggregate, - "grant", "org3", []string{}, - ), - )(t)}).reduceAdded, + es: newMockEventStore().appendFilterResponse( + []eventstore.Event{ + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user-id", "org1").Aggregate, + "username1", + "firstname1", + "lastname1", + "nickname1", + "displayname1", + language.German, + domain.GenderMale, + "email1", + true, + ), + }, + ).appendFilterResponse( + []eventstore.Event{ + project.NewGrantAddedEvent(context.Background(), + &project.NewAggregate("project1", "org2").Aggregate, + "grant", "org3", []string{}, + ), + }, + ), + }).reduceAdded, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -72,7 +77,7 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { "user-id", "org1", false, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, anyArg{}, anyArg{}, uint64(15), @@ -92,27 +97,27 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project GrantMemberChangedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantMemberChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.GrantMemberChangedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role", "changed"], "grantId": "grant-id" }`), - ), project.GrantMemberChangedEventMapper), + ), project.GrantMemberChangedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceChanged, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.project_grant_members3 SET (roles, change_date, sequence) = ($1, $2, $3) WHERE (instance_id = $4) AND (user_id = $5) AND (project_id = $6) AND (grant_id = $7)", expectedArgs: []interface{}{ - database.StringArray{"role", "changed"}, + database.TextArray[string]{"role", "changed"}, anyArg{}, uint64(15), "instance-id", @@ -128,20 +133,20 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project GrantMemberCascadeRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantMemberCascadeRemovedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.GrantMemberCascadeRemovedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "grantId": "grant-id" }`), - ), project.GrantMemberCascadeRemovedEventMapper), + ), project.GrantMemberCascadeRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -160,20 +165,20 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project GrantMemberRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantMemberRemovedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.GrantMemberRemovedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "grantId": "grant-id" }`), - ), project.GrantMemberRemovedEventMapper), + ), project.GrantMemberRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -192,17 +197,17 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "user UserRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -219,17 +224,17 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project ProjectRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - []byte(`{}`), - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + []byte(`{}`), + ), project.ProjectRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -245,17 +250,17 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { }, { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -271,17 +276,17 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "project GrantRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantRemovedType), - project.AggregateType, - []byte(`{"grantId": "grant-id"}`), - ), project.GrantRemovedEventMapper), + event: getEvent( + testEvent( + project.GrantRemovedType, + project.AggregateType, + []byte(`{"grantId": "grant-id"}`), + ), project.GrantRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceProjectGrantRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -299,17 +304,17 @@ func TestProjectGrantMemberProjection_reduces(t *testing.T) { { name: "org OrgRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - []byte(`{}`), - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + []byte(`{}`), + ), org.OrgRemovedEventMapper), }, reduce: (&projectGrantMemberProjection{}).reduceOrgRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/project_grant_test.go b/internal/query/projection/project_grant_test.go index c7ef0532d..485e4aaa0 100644 --- a/internal/query/projection/project_grant_test.go +++ b/internal/query/projection/project_grant_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -27,17 +26,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - nil, - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + nil, + ), project.ProjectRemovedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -54,17 +53,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(ProjectGrantColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -80,17 +79,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantRemovedType), - project.AggregateType, - []byte(`{"grantId": "grant-id"}`), - ), project.GrantRemovedEventMapper), + event: getEvent( + testEvent( + project.GrantRemovedType, + project.AggregateType, + []byte(`{"grantId": "grant-id"}`), + ), project.GrantRemovedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -108,17 +107,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantReactivatedType), - project.AggregateType, - []byte(`{"grantId": "grant-id"}`), - ), project.GrantReactivatedEventMapper), + event: getEvent( + testEvent( + project.GrantReactivatedType, + project.AggregateType, + []byte(`{"grantId": "grant-id"}`), + ), project.GrantReactivatedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -139,17 +138,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantDeactivatedType), - project.AggregateType, - []byte(`{"grantId": "grant-id"}`), - ), project.GrantDeactivateEventMapper), + event: getEvent( + testEvent( + project.GrantDeactivatedType, + project.AggregateType, + []byte(`{"grantId": "grant-id"}`), + ), project.GrantDeactivateEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -170,17 +169,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantChangedType), - project.AggregateType, - []byte(`{"grantId": "grant-id", "roleKeys": ["admin", "user"] }`), - ), project.GrantChangedEventMapper), + event: getEvent( + testEvent( + project.GrantChangedType, + project.AggregateType, + []byte(`{"grantId": "grant-id", "roleKeys": ["admin", "user"] }`), + ), project.GrantChangedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -188,7 +187,7 @@ func TestProjectGrantProjection_reduces(t *testing.T) { expectedArgs: []interface{}{ anyArg{}, uint64(15), - database.StringArray{"admin", "user"}, + database.TextArray[string]{"admin", "user"}, "grant-id", "agg-id", "instance-id", @@ -201,17 +200,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantCascadeChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantCascadeChangedType), - project.AggregateType, - []byte(`{"grantId": "grant-id", "roleKeys": ["admin", "user"] }`), - ), project.GrantCascadeChangedEventMapper), + event: getEvent( + testEvent( + project.GrantCascadeChangedType, + project.AggregateType, + []byte(`{"grantId": "grant-id", "roleKeys": ["admin", "user"] }`), + ), project.GrantCascadeChangedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantCascadeChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -219,7 +218,7 @@ func TestProjectGrantProjection_reduces(t *testing.T) { expectedArgs: []interface{}{ anyArg{}, uint64(15), - database.StringArray{"admin", "user"}, + database.TextArray[string]{"admin", "user"}, "grant-id", "agg-id", "instance-id", @@ -232,17 +231,17 @@ func TestProjectGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantAddedType), - project.AggregateType, - []byte(`{"grantId": "grant-id", "grantedOrgId": "granted-org-id", "roleKeys": ["admin", "user"] }`), - ), project.GrantAddedEventMapper), + event: getEvent( + testEvent( + project.GrantAddedType, + project.AggregateType, + []byte(`{"grantId": "grant-id", "grantedOrgId": "granted-org-id", "roleKeys": ["admin", "user"] }`), + ), project.GrantAddedEventMapper), }, reduce: (&projectGrantProjection{}).reduceProjectGrantAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -257,7 +256,7 @@ func TestProjectGrantProjection_reduces(t *testing.T) { domain.ProjectGrantStateActive, uint64(15), "granted-org-id", - database.StringArray{"admin", "user"}, + database.TextArray[string]{"admin", "user"}, }, }, }, @@ -268,16 +267,16 @@ func TestProjectGrantProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&projectGrantProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/project_member.go b/internal/query/projection/project_member.go index 4039f8284..e6ad90f73 100644 --- a/internal/query/projection/project_member.go +++ b/internal/query/projection/project_member.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/member" "github.com/zitadel/zitadel/internal/repository/org" @@ -20,34 +20,49 @@ const ( ) type projectMemberProjection struct { - crdb.StatementHandler + es handler.EventStore } -func newProjectMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *projectMemberProjection { - p := new(projectMemberProjection) - config.ProjectionName = ProjectMemberProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable( +func newProjectMemberProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, &projectMemberProjection{es: config.Eventstore}) +} + +func (*projectMemberProjection) Name() string { + return ProjectMemberProjectionTable +} + +func (*projectMemberProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable( append(memberColumns, - crdb.NewColumn(ProjectMemberProjectIDCol, crdb.ColumnTypeText), + handler.NewColumn(ProjectMemberProjectIDCol, handler.ColumnTypeText), + ), + handler.NewPrimaryKey(MemberInstanceID, ProjectMemberProjectIDCol, MemberUserIDCol), + handler.WithIndex(handler.NewIndex("user_id", []string{MemberUserIDCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{MemberOwnerRemoved})), + handler.WithIndex(handler.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), + handler.WithIndex( + handler.NewIndex("pm_instance", []string{MemberInstanceID}, + handler.WithInclude( + MemberCreationDate, + MemberChangeDate, + MemberUserOwnerRemoved, + MemberRolesCol, + MemberSequence, + MemberResourceOwner, + MemberOwnerRemoved, + ), + ), ), - crdb.NewPrimaryKey(MemberInstanceID, ProjectMemberProjectIDCol, MemberUserIDCol), - crdb.WithIndex(crdb.NewIndex("user_id", []string{MemberUserIDCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{MemberOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("user_owner_removed", []string{MemberUserOwnerRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *projectMemberProjection) reducers() []handler.AggregateReducer { +func (p *projectMemberProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.MemberAddedType, Reduce: p.reduceAdded, @@ -72,7 +87,7 @@ func (p *projectMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, @@ -81,7 +96,7 @@ func (p *projectMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOrgRemoved, @@ -90,7 +105,7 @@ func (p *projectMemberProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(MemberInstanceID), @@ -106,7 +121,7 @@ func (p *projectMemberProjection) reduceAdded(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-bgx5Q", "reduce.wrong.event.type %s", project.MemberAddedType) } ctx := setMemberContext(e.Aggregate()) - userOwner, err := getResourceOwnerOfUser(ctx, p.Eventstore, e.Aggregate().InstanceID, e.UserID) + userOwner, err := getResourceOwnerOfUser(ctx, p.es, e.Aggregate().InstanceID, e.UserID) if err != nil { return nil, err } @@ -163,7 +178,7 @@ func (p *projectMemberProjection) reduceOrgRemoved(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-NGUEL", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, multiReduceMemberOwnerRemoved(e), multiReduceMemberUserOwnerRemoved(e), diff --git a/internal/query/projection/project_member_test.go b/internal/query/projection/project_member_test.go index 4c0f1ee0c..27925c51e 100644 --- a/internal/query/projection/project_member_test.go +++ b/internal/query/projection/project_member_test.go @@ -10,8 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -31,34 +30,37 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "project MemberAddedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.MemberAddedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.MemberAddedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role"] }`), - ), project.MemberAddedEventMapper), + ), project.MemberAddedEventMapper), }, reduce: (&projectMemberProjection{ - StatementHandler: getStatementHandlerWithFilters( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user-id", "org1").Aggregate, - "username1", - "firstname1", - "lastname1", - "nickname1", - "displayname1", - language.German, - domain.GenderMale, - "email1", - true, - ), - )(t)}).reduceAdded, + es: newMockEventStore().appendFilterResponse( + []eventstore.Event{ + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user-id", "org1").Aggregate, + "username1", + "firstname1", + "lastname1", + "nickname1", + "displayname1", + language.German, + domain.GenderMale, + "email1", + true, + ), + }, + ), + }).reduceAdded, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -67,7 +69,7 @@ func TestProjectMemberProjection_reduces(t *testing.T) { "user-id", "org1", false, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, anyArg{}, anyArg{}, uint64(15), @@ -84,26 +86,26 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "project MemberChangedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.MemberChangedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.MemberChangedType, + project.AggregateType, + []byte(`{ "userId": "user-id", "roles": ["role", "changed"] }`), - ), project.MemberChangedEventMapper), + ), project.MemberChangedEventMapper), }, reduce: (&projectMemberProjection{}).reduceChanged, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.project_members3 SET (roles, change_date, sequence) = ($1, $2, $3) WHERE (instance_id = $4) AND (user_id = $5) AND (project_id = $6)", expectedArgs: []interface{}{ - database.StringArray{"role", "changed"}, + database.TextArray[string]{"role", "changed"}, anyArg{}, uint64(15), "instance-id", @@ -118,19 +120,19 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "project MemberCascadeRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.MemberCascadeRemovedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.MemberCascadeRemovedType, + project.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), project.MemberCascadeRemovedEventMapper), + ), project.MemberCascadeRemovedEventMapper), }, reduce: (&projectMemberProjection{}).reduceCascadeRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -148,19 +150,19 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "project MemberRemovedType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.MemberRemovedType), - project.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + project.MemberRemovedType, + project.AggregateType, + []byte(`{ "userId": "user-id" }`), - ), project.MemberRemovedEventMapper), + ), project.MemberRemovedEventMapper), }, reduce: (&projectMemberProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -178,17 +180,17 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "user UserRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&projectMemberProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -205,17 +207,17 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "project ProjectRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - []byte(`{}`), - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + []byte(`{}`), + ), project.ProjectRemovedEventMapper), }, reduce: (&projectMemberProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -232,17 +234,17 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -258,17 +260,17 @@ func TestProjectMemberProjection_reduces(t *testing.T) { { name: "org OrgRemovedEventType", args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - []byte(`{}`), - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + []byte(`{}`), + ), org.OrgRemovedEventMapper), }, reduce: (&projectMemberProjection{}).reduceOrgRemoved, want: wantReduce{ - aggregateType: org.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: org.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/project_role.go b/internal/query/projection/project_role.go index 4287414f2..b4fcd21f6 100644 --- a/internal/query/projection/project_role.go +++ b/internal/query/projection/project_role.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -27,40 +27,41 @@ const ( ProjectRoleColumnOwnerRemoved = "owner_removed" ) -type projectRoleProjection struct { - crdb.StatementHandler +type projectRoleProjection struct{} + +func newProjectRoleProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(projectRoleProjection)) } -func newProjectRoleProjection(ctx context.Context, config crdb.StatementHandlerConfig) *projectRoleProjection { - p := new(projectRoleProjection) - config.ProjectionName = ProjectRoleProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(ProjectRoleColumnProjectID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnKey, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectRoleColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(ProjectRoleColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(ProjectRoleColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnDisplayName, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnGroupName, crdb.ColumnTypeText), - crdb.NewColumn(ProjectRoleColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*projectRoleProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(ProjectRoleColumnProjectID, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnKey, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectRoleColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(ProjectRoleColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(ProjectRoleColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnDisplayName, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnGroupName, handler.ColumnTypeText), + handler.NewColumn(ProjectRoleColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(ProjectRoleColumnInstanceID, ProjectRoleColumnProjectID, ProjectRoleColumnKey), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{ProjectRoleColumnOwnerRemoved})), + handler.NewPrimaryKey(ProjectRoleColumnInstanceID, ProjectRoleColumnProjectID, ProjectRoleColumnKey), + handler.WithIndex(handler.NewIndex("owner_removed", []string{ProjectRoleColumnOwnerRemoved})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *projectRoleProjection) reducers() []handler.AggregateReducer { +func (*projectRoleProjection) Name() string { + return ProjectRoleProjectionTable +} + +func (p *projectRoleProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.RoleAddedType, Reduce: p.reduceProjectRoleAdded, @@ -81,7 +82,7 @@ func (p *projectRoleProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -90,7 +91,7 @@ func (p *projectRoleProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(ProjectRoleColumnInstanceID), @@ -105,7 +106,7 @@ func (p *projectRoleProjection) reduceProjectRoleAdded(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-g92Fg", "reduce.wrong.event.type %s", project.RoleAddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(ProjectRoleColumnKey, e.Key), @@ -127,7 +128,7 @@ func (p *projectRoleProjection) reduceProjectRoleChanged(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-sM0f", "reduce.wrong.event.type %s", project.GrantChangedType) } if e.DisplayName == nil && e.Group == nil { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } columns := make([]handler.Column, 0, 7) columns = append(columns, handler.NewCol(ProjectRoleColumnChangeDate, e.CreationDate()), @@ -138,7 +139,7 @@ func (p *projectRoleProjection) reduceProjectRoleChanged(event eventstore.Event) if e.Group != nil { columns = append(columns, handler.NewCol(ProjectRoleColumnGroupName, *e.Group)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, columns, []handler.Condition{ @@ -154,7 +155,7 @@ func (p *projectRoleProjection) reduceProjectRoleRemoved(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-L0fJf", "reduce.wrong.event.type %s", project.GrantRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectRoleColumnKey, e.Key), @@ -169,7 +170,7 @@ func (p *projectRoleProjection) reduceProjectRemoved(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-l0geG", "reduce.wrong.event.type %s", project.ProjectRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectRoleColumnProjectID, e.Aggregate().ID), @@ -184,7 +185,7 @@ func (p *projectRoleProjection) reduceOwnerRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-3XrHY", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(ProjectRoleColumnInstanceID, e.Aggregate().InstanceID), diff --git a/internal/query/projection/project_role_test.go b/internal/query/projection/project_role_test.go index 0f54eb34e..684ffa967 100644 --- a/internal/query/projection/project_role_test.go +++ b/internal/query/projection/project_role_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -25,17 +24,17 @@ func TestProjectRoleProjection_reduces(t *testing.T) { { name: "reduceProjectRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - nil, - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + nil, + ), project.ProjectRemovedEventMapper), }, reduce: (&projectRoleProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -52,17 +51,17 @@ func TestProjectRoleProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(ProjectRoleColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -78,17 +77,17 @@ func TestProjectRoleProjection_reduces(t *testing.T) { { name: "reduceProjectRoleRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.RoleRemovedType), - project.AggregateType, - []byte(`{"key": "key"}`), - ), project.RoleRemovedEventMapper), + event: getEvent( + testEvent( + project.RoleRemovedType, + project.AggregateType, + []byte(`{"key": "key"}`), + ), project.RoleRemovedEventMapper), }, reduce: (&projectRoleProjection{}).reduceProjectRoleRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -106,17 +105,17 @@ func TestProjectRoleProjection_reduces(t *testing.T) { { name: "reduceProjectRoleChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.RoleChangedType), - project.AggregateType, - []byte(`{"key": "key", "displayName": "New Key", "group": "New Group"}`), - ), project.RoleChangedEventMapper), + event: getEvent( + testEvent( + project.RoleChangedType, + project.AggregateType, + []byte(`{"key": "key", "displayName": "New Key", "group": "New Group"}`), + ), project.RoleChangedEventMapper), }, reduce: (&projectRoleProjection{}).reduceProjectRoleChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -138,34 +137,34 @@ func TestProjectRoleProjection_reduces(t *testing.T) { { name: "reduceProjectRoleChanged no changes", args: args{ - event: getEvent(testEvent( - repository.EventType(project.RoleChangedType), - project.AggregateType, - []byte(`{}`), - ), project.RoleChangedEventMapper), + event: getEvent( + testEvent( + project.RoleChangedType, + project.AggregateType, + []byte(`{}`), + ), project.RoleChangedEventMapper), }, reduce: (&projectRoleProjection{}).reduceProjectRoleChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "reduceProjectRoleAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.RoleAddedType), - project.AggregateType, - []byte(`{"key": "key", "displayName": "Key", "group": "Group"}`), - ), project.RoleAddedEventMapper), + event: getEvent( + testEvent( + project.RoleAddedType, + project.AggregateType, + []byte(`{"key": "key", "displayName": "Key", "group": "Group"}`), + ), project.RoleAddedEventMapper), }, reduce: (&projectRoleProjection{}).reduceProjectRoleAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -190,16 +189,16 @@ func TestProjectRoleProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&projectRoleProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/project_test.go b/internal/query/projection/project_test.go index a9c05d88c..4b5756b0e 100644 --- a/internal/query/projection/project_test.go +++ b/internal/query/projection/project_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -26,17 +25,17 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "reduceProjectRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - nil, - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + nil, + ), project.ProjectRemovedEventMapper), }, reduce: (&projectProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -53,17 +52,17 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(ProjectColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -79,17 +78,17 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "reduceProjectReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectReactivatedType), - project.AggregateType, - nil, - ), project.ProjectReactivatedEventMapper), + event: getEvent( + testEvent( + project.ProjectReactivatedType, + project.AggregateType, + nil, + ), project.ProjectReactivatedEventMapper), }, reduce: (&projectProjection{}).reduceProjectReactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -109,17 +108,17 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "reduceProjectDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectDeactivatedType), - project.AggregateType, - nil, - ), project.ProjectDeactivatedEventMapper), + event: getEvent( + testEvent( + project.ProjectDeactivatedType, + project.AggregateType, + nil, + ), project.ProjectDeactivatedEventMapper), }, reduce: (&projectProjection{}).reduceProjectDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -139,17 +138,17 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "reduceProjectChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectChangedType), - project.AggregateType, - []byte(`{"name": "new name", "projectRoleAssertion": true, "projectRoleCheck": true, "hasProjectCheck": true, "privateLabelingSetting": 1}`), - ), project.ProjectChangeEventMapper), + event: getEvent( + testEvent( + project.ProjectChangedType, + project.AggregateType, + []byte(`{"name": "new name", "projectRoleAssertion": true, "projectRoleCheck": true, "hasProjectCheck": true, "privateLabelingSetting": 1}`), + ), project.ProjectChangeEventMapper), }, reduce: (&projectProjection{}).reduceProjectChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -173,34 +172,34 @@ func TestProjectProjection_reduces(t *testing.T) { { name: "reduceProjectChanged no changes", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectChangedType), - project.AggregateType, - []byte(`{}`), - ), project.ProjectChangeEventMapper), + event: getEvent( + testEvent( + project.ProjectChangedType, + project.AggregateType, + []byte(`{}`), + ), project.ProjectChangeEventMapper), }, reduce: (&projectProjection{}).reduceProjectChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, - executer: &testExecuter{}, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + executer: &testExecuter{}, }, }, { name: "reduceProjectAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectAddedType), - project.AggregateType, - []byte(`{"name": "name", "projectRoleAssertion": true, "projectRoleCheck": true, "hasProjectCheck": true, "privateLabelingSetting": 1}`), - ), project.ProjectAddedEventMapper), + event: getEvent( + testEvent( + project.ProjectAddedType, + project.AggregateType, + []byte(`{"name": "name", "projectRoleAssertion": true, "projectRoleCheck": true, "hasProjectCheck": true, "privateLabelingSetting": 1}`), + ), project.ProjectAddedEventMapper), }, reduce: (&projectProjection{}).reduceProjectAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("project"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("project"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -228,16 +227,16 @@ func TestProjectProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&projectProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index 19a0c1cc0..845395ec2 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -3,77 +3,76 @@ package projection import ( "context" + internal_authz "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" ) const ( - CurrentSeqTable = "projections.current_sequences" + CurrentStateTable = "projections.current_states" LocksTable = "projections.locks" - FailedEventsTable = "projections.failed_events" + FailedEventsTable = "projections.failed_events2" ) var ( - projectionConfig crdb.StatementHandlerConfig - OrgProjection *orgProjection - OrgMetadataProjection *orgMetadataProjection - ActionProjection *actionProjection - FlowProjection *flowProjection - ProjectProjection *projectProjection - PasswordComplexityProjection *passwordComplexityProjection - PasswordAgeProjection *passwordAgeProjection - LockoutPolicyProjection *lockoutPolicyProjection - PrivacyPolicyProjection *privacyPolicyProjection - DomainPolicyProjection *domainPolicyProjection - LabelPolicyProjection *labelPolicyProjection - ProjectGrantProjection *projectGrantProjection - ProjectRoleProjection *projectRoleProjection - OrgDomainProjection *orgDomainProjection - LoginPolicyProjection *loginPolicyProjection - IDPProjection *idpProjection - AppProjection *appProjection - IDPUserLinkProjection *idpUserLinkProjection - IDPLoginPolicyLinkProjection *idpLoginPolicyLinkProjection - IDPTemplateProjection *idpTemplateProjection - MailTemplateProjection *mailTemplateProjection - MessageTextProjection *messageTextProjection - CustomTextProjection *customTextProjection - UserProjection *userProjection - LoginNameProjection *loginNameProjection - OrgMemberProjection *orgMemberProjection - InstanceDomainProjection *instanceDomainProjection - InstanceMemberProjection *instanceMemberProjection - ProjectMemberProjection *projectMemberProjection - ProjectGrantMemberProjection *projectGrantMemberProjection - AuthNKeyProjection *authNKeyProjection - PersonalAccessTokenProjection *personalAccessTokenProjection - UserGrantProjection *userGrantProjection - UserMetadataProjection *userMetadataProjection - UserAuthMethodProjection *userAuthMethodProjection - InstanceProjection *instanceProjection - SecretGeneratorProjection *secretGeneratorProjection - SMTPConfigProjection *smtpConfigProjection - SMSConfigProjection *smsConfigProjection - OIDCSettingsProjection *oidcSettingsProjection - DebugNotificationProviderProjection *debugNotificationProviderProjection - KeyProjection *keyProjection - SecurityPolicyProjection *securityPolicyProjection - NotificationPolicyProjection *notificationPolicyProjection + projectionConfig handler.Config + OrgProjection *handler.Handler + OrgMetadataProjection *handler.Handler + ActionProjection *handler.Handler + FlowProjection *handler.Handler + ProjectProjection *handler.Handler + PasswordComplexityProjection *handler.Handler + PasswordAgeProjection *handler.Handler + LockoutPolicyProjection *handler.Handler + PrivacyPolicyProjection *handler.Handler + DomainPolicyProjection *handler.Handler + LabelPolicyProjection *handler.Handler + ProjectGrantProjection *handler.Handler + ProjectRoleProjection *handler.Handler + OrgDomainProjection *handler.Handler + LoginPolicyProjection *handler.Handler + IDPProjection *handler.Handler + AppProjection *handler.Handler + IDPUserLinkProjection *handler.Handler + IDPLoginPolicyLinkProjection *handler.Handler + IDPTemplateProjection *handler.Handler + MailTemplateProjection *handler.Handler + MessageTextProjection *handler.Handler + CustomTextProjection *handler.Handler + UserProjection *handler.Handler + LoginNameProjection *handler.Handler + OrgMemberProjection *handler.Handler + InstanceDomainProjection *handler.Handler + InstanceMemberProjection *handler.Handler + ProjectMemberProjection *handler.Handler + ProjectGrantMemberProjection *handler.Handler + AuthNKeyProjection *handler.Handler + PersonalAccessTokenProjection *handler.Handler + UserGrantProjection *handler.Handler + UserMetadataProjection *handler.Handler + UserAuthMethodProjection *handler.Handler + InstanceProjection *handler.Handler + SecretGeneratorProjection *handler.Handler + SMTPConfigProjection *handler.Handler + SMSConfigProjection *handler.Handler + OIDCSettingsProjection *handler.Handler + DebugNotificationProviderProjection *handler.Handler + KeyProjection *handler.Handler + SecurityPolicyProjection *handler.Handler + NotificationPolicyProjection *handler.Handler NotificationsProjection interface{} NotificationsQuotaProjection interface{} TelemetryPusherProjection interface{} - DeviceAuthProjection *deviceAuthProjection - SessionProjection *sessionProjection - AuthRequestProjection *authRequestProjection - MilestoneProjection *milestoneProjection + DeviceAuthProjection *handler.Handler + SessionProjection *handler.Handler + AuthRequestProjection *handler.Handler + MilestoneProjection *handler.Handler QuotaProjection *quotaProjection ) type projection interface { - Start() + Start(ctx context.Context) Init(ctx context.Context) error } @@ -81,24 +80,16 @@ var ( projections []projection ) -func Create(ctx context.Context, sqlClient *database.DB, es *eventstore.Eventstore, config Config, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, certEncryptionAlgorithm crypto.EncryptionAlgorithm) error { - projectionConfig = crdb.StatementHandlerConfig{ - ProjectionHandlerConfig: handler.ProjectionHandlerConfig{ - HandlerConfig: handler.HandlerConfig{ - Eventstore: es, - }, - RequeueEvery: config.RequeueEvery, - RetryFailedAfter: config.RetryFailedAfter, - Retries: config.MaxFailureCount, - ConcurrentInstances: config.ConcurrentInstances, - HandleActiveInstances: config.HandleActiveInstances, - }, - Client: sqlClient, - SequenceTable: CurrentSeqTable, - LockTable: LocksTable, - FailedEventsTable: FailedEventsTable, - MaxFailureCount: config.MaxFailureCount, - BulkLimit: config.BulkLimit, +func Create(ctx context.Context, sqlClient *database.DB, es handler.EventStore, config Config, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, certEncryptionAlgorithm crypto.EncryptionAlgorithm, systemUsers map[string]*internal_authz.SystemAPIUser) error { + projectionConfig = handler.Config{ + Client: sqlClient, + Eventstore: es, + BulkLimit: uint16(config.BulkLimit), + RequeueEvery: config.RequeueEvery, + HandleActiveInstances: config.HandleActiveInstances, + MaxFailureCount: config.MaxFailureCount, + RetryFailedAfter: config.RetryFailedAfter, + TransactionDuration: config.TransactionDuration, } OrgProjection = newOrgProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["orgs"])) @@ -148,7 +139,7 @@ func Create(ctx context.Context, sqlClient *database.DB, es *eventstore.Eventsto DeviceAuthProjection = newDeviceAuthProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["device_auth"])) SessionProjection = newSessionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sessions"])) AuthRequestProjection = newAuthRequestProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["auth_requests"])) - MilestoneProjection = newMilestoneProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["milestones"])) + MilestoneProjection = newMilestoneProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["milestones"]), systemUsers) QuotaProjection = newQuotaProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["quotas"])) newProjectionsList() return nil @@ -163,17 +154,17 @@ func Init(ctx context.Context) error { return nil } -func Start() { +func Start(ctx context.Context) { for _, projection := range projections { - projection.Start() + projection.Start(ctx) } } -func ApplyCustomConfig(customConfig CustomConfig) crdb.StatementHandlerConfig { +func ApplyCustomConfig(customConfig CustomConfig) handler.Config { return applyCustomConfig(projectionConfig, customConfig) } -func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomConfig) crdb.StatementHandlerConfig { +func applyCustomConfig(config handler.Config, customConfig CustomConfig) handler.Config { if customConfig.BulkLimit != nil { config.BulkLimit = *customConfig.BulkLimit } @@ -189,6 +180,9 @@ func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomCo if customConfig.HandleActiveInstances != nil { config.HandleActiveInstances = *customConfig.HandleActiveInstances } + if customConfig.TransactionDuration != nil { + config.TransactionDuration = *customConfig.TransactionDuration + } return config } @@ -249,6 +243,6 @@ func newProjectionsList() { SessionProjection, AuthRequestProjection, MilestoneProjection, - QuotaProjection, + QuotaProjection.handler, } } diff --git a/internal/query/projection/quota.go b/internal/query/projection/quota.go index e6de8663a..d92ad0b49 100644 --- a/internal/query/projection/quota.go +++ b/internal/query/projection/quota.go @@ -7,8 +7,8 @@ import ( "github.com/zitadel/zitadel/internal/database" zitadel_errors "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/quota" ) @@ -51,62 +51,68 @@ const ( ) type quotaProjection struct { - crdb.StatementHandler - client *database.DB + handler *handler.Handler + client *database.DB } -func newQuotaProjection(ctx context.Context, config crdb.StatementHandlerConfig) *quotaProjection { - p := new(quotaProjection) - config.ProjectionName = QuotasProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable( - []*crdb.Column{ - crdb.NewColumn(QuotaColumnID, crdb.ColumnTypeText), - crdb.NewColumn(QuotaColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(QuotaColumnUnit, crdb.ColumnTypeEnum), - crdb.NewColumn(QuotaColumnAmount, crdb.ColumnTypeInt64, crdb.Nullable()), - crdb.NewColumn(QuotaColumnFrom, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(QuotaColumnInterval, crdb.ColumnTypeInterval, crdb.Nullable()), - crdb.NewColumn(QuotaColumnLimit, crdb.ColumnTypeBool, crdb.Nullable()), - }, - crdb.NewPrimaryKey(QuotaColumnInstanceID, QuotaColumnUnit), - ), - crdb.NewSuffixedTable( - []*crdb.Column{ - crdb.NewColumn(QuotaPeriodColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(QuotaPeriodColumnUnit, crdb.ColumnTypeEnum), - crdb.NewColumn(QuotaPeriodColumnStart, crdb.ColumnTypeTimestamp), - crdb.NewColumn(QuotaPeriodColumnUsage, crdb.ColumnTypeInt64), - }, - crdb.NewPrimaryKey(QuotaPeriodColumnInstanceID, QuotaPeriodColumnUnit, QuotaPeriodColumnStart), - quotaPeriodsTableSuffix, - ), - crdb.NewSuffixedTable( - []*crdb.Column{ - crdb.NewColumn(QuotaNotificationColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(QuotaNotificationColumnUnit, crdb.ColumnTypeEnum), - crdb.NewColumn(QuotaNotificationColumnID, crdb.ColumnTypeText), - crdb.NewColumn(QuotaNotificationColumnCallURL, crdb.ColumnTypeText), - crdb.NewColumn(QuotaNotificationColumnPercent, crdb.ColumnTypeInt64), - crdb.NewColumn(QuotaNotificationColumnRepeat, crdb.ColumnTypeBool), - crdb.NewColumn(QuotaNotificationColumnLatestDuePeriodStart, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(QuotaNotificationColumnNextDueThreshold, crdb.ColumnTypeInt64, crdb.Nullable()), - }, - crdb.NewPrimaryKey(QuotaNotificationColumnInstanceID, QuotaNotificationColumnUnit, QuotaNotificationColumnID), - quotaNotificationsTableSuffix, - ), - ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - p.client = config.Client +func newQuotaProjection(ctx context.Context, config handler.Config) *quotaProjection { + p := "aProjection{ + client: config.Client, + } + p.handler = handler.NewHandler(ctx, &config, p) return p } -func (q *quotaProjection) reducers() []handler.AggregateReducer { +func (*quotaProjection) Name() string { + return QuotasProjectionTable +} + +func (*quotaProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable( + []*handler.InitColumn{ + handler.NewColumn(QuotaColumnID, handler.ColumnTypeText), + handler.NewColumn(QuotaColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(QuotaColumnUnit, handler.ColumnTypeEnum), + handler.NewColumn(QuotaColumnAmount, handler.ColumnTypeInt64, handler.Nullable()), + handler.NewColumn(QuotaColumnFrom, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(QuotaColumnInterval, handler.ColumnTypeInterval, handler.Nullable()), + handler.NewColumn(QuotaColumnLimit, handler.ColumnTypeBool, handler.Nullable()), + }, + handler.NewPrimaryKey(QuotaColumnInstanceID, QuotaColumnUnit), + ), + handler.NewSuffixedTable( + []*handler.InitColumn{ + handler.NewColumn(QuotaPeriodColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(QuotaPeriodColumnUnit, handler.ColumnTypeEnum), + handler.NewColumn(QuotaPeriodColumnStart, handler.ColumnTypeTimestamp), + handler.NewColumn(QuotaPeriodColumnUsage, handler.ColumnTypeInt64), + }, + handler.NewPrimaryKey(QuotaPeriodColumnInstanceID, QuotaPeriodColumnUnit, QuotaPeriodColumnStart), + quotaPeriodsTableSuffix, + ), + handler.NewSuffixedTable( + []*handler.InitColumn{ + handler.NewColumn(QuotaNotificationColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(QuotaNotificationColumnUnit, handler.ColumnTypeEnum), + handler.NewColumn(QuotaNotificationColumnID, handler.ColumnTypeText), + handler.NewColumn(QuotaNotificationColumnCallURL, handler.ColumnTypeText), + handler.NewColumn(QuotaNotificationColumnPercent, handler.ColumnTypeInt64), + handler.NewColumn(QuotaNotificationColumnRepeat, handler.ColumnTypeBool), + handler.NewColumn(QuotaNotificationColumnLatestDuePeriodStart, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(QuotaNotificationColumnNextDueThreshold, handler.ColumnTypeInt64, handler.Nullable()), + }, + handler.NewPrimaryKey(QuotaNotificationColumnInstanceID, QuotaNotificationColumnUnit, QuotaNotificationColumnID), + quotaNotificationsTableSuffix, + ), + ) +} + +func (q *quotaProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: q.reduceInstanceRemoved, @@ -115,7 +121,7 @@ func (q *quotaProjection) reducers() []handler.AggregateReducer { }, { Aggregate: quota.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: quota.AddedEventType, Reduce: q.reduceQuotaSet, @@ -142,7 +148,7 @@ func (q *quotaProjection) reducers() []handler.AggregateReducer { } func (q *quotaProjection) reduceQuotaNotified(event eventstore.Event) (*handler.Statement, error) { - return crdb.NewNoOpStatement(event), nil + return handler.NewNoOpStatement(event), nil } func (q *quotaProjection) reduceQuotaSet(event eventstore.Event) (*handler.Statement, error) { @@ -150,7 +156,7 @@ func (q *quotaProjection) reduceQuotaSet(event eventstore.Event) (*handler.State if err != nil { return nil, err } - var statements []func(e eventstore.Event) crdb.Exec + var statements []func(e eventstore.Event) handler.Exec // 1. Insert or update quota if the event has not only notification changes quotaConflictColumns := []handler.Column{ @@ -174,24 +180,24 @@ func (q *quotaProjection) reduceQuotaSet(event eventstore.Event) (*handler.State // TODO: Add the quota ID to the primary key in a migration? quotaUpdateCols = append(quotaUpdateCols, handler.NewCol(QuotaColumnID, e.Aggregate().ID)) quotaUpdateCols = append(quotaUpdateCols, quotaConflictColumns...) - statements = append(statements, crdb.AddUpsertStatement(quotaConflictColumns, quotaUpdateCols)) + statements = append(statements, handler.AddUpsertStatement(quotaConflictColumns, quotaUpdateCols)) } // 2. Delete existing notifications if e.Notifications == nil { - return crdb.NewMultiStatement(e, statements...), nil + return handler.NewMultiStatement(e, statements...), nil } - statements = append(statements, crdb.AddDeleteStatement( + statements = append(statements, handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(QuotaNotificationColumnUnit, e.Unit), }, - crdb.WithTableSuffix(quotaNotificationsTableSuffix), + handler.WithTableSuffix(quotaNotificationsTableSuffix), )) notifications := *e.Notifications for i := range notifications { notification := notifications[i] - statements = append(statements, crdb.AddCreateStatement( + statements = append(statements, handler.AddCreateStatement( []handler.Column{ handler.NewCol(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(QuotaNotificationColumnUnit, e.Unit), @@ -200,10 +206,10 @@ func (q *quotaProjection) reduceQuotaSet(event eventstore.Event) (*handler.State handler.NewCol(QuotaNotificationColumnPercent, notification.Percent), handler.NewCol(QuotaNotificationColumnRepeat, notification.Repeat), }, - crdb.WithTableSuffix(quotaNotificationsTableSuffix), + handler.WithTableSuffix(quotaNotificationsTableSuffix), )) } - return crdb.NewMultiStatement(e, statements...), nil + return handler.NewMultiStatement(e, statements...), nil } func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*handler.Statement, error) { @@ -211,7 +217,7 @@ func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*h if err != nil { return nil, err } - return crdb.NewUpdateStatement(e, + return handler.NewUpdateStatement(e, []handler.Column{ handler.NewCol(QuotaNotificationColumnLatestDuePeriodStart, e.PeriodStart), handler.NewCol(QuotaNotificationColumnNextDueThreshold, e.Threshold+100), // next due_threshold is always the reached + 100 => percent (e.g. 90) in the next bucket (e.g. 190) @@ -221,9 +227,7 @@ func (q *quotaProjection) reduceQuotaNotificationDue(event eventstore.Event) (*h handler.NewCond(QuotaNotificationColumnUnit, e.Unit), handler.NewCond(QuotaNotificationColumnID, e.ID), }, - crdb.WithTableSuffix(quotaNotificationsTableSuffix), - // The notification could have been removed in the meantime - crdb.WithIgnoreNotFound(), + handler.WithTableSuffix(quotaNotificationsTableSuffix), ), nil } @@ -232,23 +236,23 @@ func (q *quotaProjection) reduceQuotaRemoved(event eventstore.Event) (*handler.S if err != nil { return nil, err } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaPeriodColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(QuotaPeriodColumnUnit, e.Unit), }, - crdb.WithTableSuffix(quotaPeriodsTableSuffix), + handler.WithTableSuffix(quotaPeriodsTableSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(QuotaNotificationColumnUnit, e.Unit), }, - crdb.WithTableSuffix(quotaNotificationsTableSuffix), + handler.WithTableSuffix(quotaNotificationsTableSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(QuotaColumnUnit, e.Unit), @@ -263,21 +267,21 @@ func (q *quotaProjection) reduceInstanceRemoved(event eventstore.Event) (*handle if err != nil { return nil, err } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaPeriodColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(quotaPeriodsTableSuffix), + handler.WithTableSuffix(quotaPeriodsTableSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaNotificationColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(quotaNotificationsTableSuffix), + handler.WithTableSuffix(quotaNotificationsTableSuffix), ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(QuotaColumnInstanceID, e.Aggregate().InstanceID), }, diff --git a/internal/query/projection/quota_test.go b/internal/query/projection/quota_test.go index 8bccec08a..fb44901e0 100644 --- a/internal/query/projection/quota_test.go +++ b/internal/query/projection/quota_test.go @@ -12,8 +12,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/quota" ) @@ -32,7 +31,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaSet with added type", args: args{ event: getEvent(testEvent( - repository.EventType(quota.AddedEventType), + quota.AddedEventType, quota.AggregateType, []byte(`{ "unit": 1, @@ -45,9 +44,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -70,7 +68,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaAdded with added type and notification", args: args{ event: getEvent(testEvent( - repository.EventType(quota.AddedEventType), + quota.AddedEventType, quota.AggregateType, []byte(`{ "unit": 1, @@ -91,9 +89,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -134,7 +131,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaSet with set type", args: args{ event: getEvent(testEvent( - repository.EventType(quota.SetEventType), + quota.SetEventType, quota.AggregateType, []byte(`{ "unit": 1, @@ -147,9 +144,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -172,7 +168,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaAdded with set type and notification", args: args{ event: getEvent(testEvent( - repository.EventType(quota.SetEventType), + quota.SetEventType, quota.AggregateType, []byte(`{ "unit": 1, @@ -193,9 +189,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -236,7 +231,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaNotificationDue", args: args{ event: getEvent(testEvent( - repository.EventType(quota.NotificationDueEventType), + quota.NotificationDueEventType, quota.AggregateType, []byte(`{ "id": "id", @@ -250,9 +245,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaNotificationDue, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -273,7 +267,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceQuotaRemoved", args: args{ event: getEvent(testEvent( - repository.EventType(quota.RemovedEventType), + quota.RemovedEventType, quota.AggregateType, []byte(`{ "unit": 1 @@ -282,9 +276,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceQuotaRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("quota"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("quota"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -315,7 +308,7 @@ func TestQuotasProjection_reduces(t *testing.T) { name: "reduceInstanceRemoved", args: args{ event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), + instance.InstanceRemovedEventType, instance.AggregateType, []byte(`{ "name": "name" @@ -324,9 +317,8 @@ func TestQuotasProjection_reduces(t *testing.T) { }, reduce: ("aProjection{}).reduceInstanceRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/secret_generator.go b/internal/query/projection/secret_generator.go index 828d0231f..d81937d67 100644 --- a/internal/query/projection/secret_generator.go +++ b/internal/query/projection/secret_generator.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -28,42 +28,43 @@ const ( SecretGeneratorColumnIncludeSymbols = "include_symbols" ) -type secretGeneratorProjection struct { - crdb.StatementHandler +type secretGeneratorProjection struct{} + +func newSecretGeneratorProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(secretGeneratorProjection)) } -func newSecretGeneratorProjection(ctx context.Context, config crdb.StatementHandlerConfig) *secretGeneratorProjection { - p := new(secretGeneratorProjection) - config.ProjectionName = SecretGeneratorProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(SecretGeneratorColumnGeneratorType, crdb.ColumnTypeEnum), - crdb.NewColumn(SecretGeneratorColumnAggregateID, crdb.ColumnTypeText), - crdb.NewColumn(SecretGeneratorColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SecretGeneratorColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SecretGeneratorColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(SecretGeneratorColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(SecretGeneratorColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(SecretGeneratorColumnLength, crdb.ColumnTypeInt64), - crdb.NewColumn(SecretGeneratorColumnExpiry, crdb.ColumnTypeInt64), - crdb.NewColumn(SecretGeneratorColumnIncludeLowerLetters, crdb.ColumnTypeBool), - crdb.NewColumn(SecretGeneratorColumnIncludeUpperLetters, crdb.ColumnTypeBool), - crdb.NewColumn(SecretGeneratorColumnIncludeDigits, crdb.ColumnTypeBool), - crdb.NewColumn(SecretGeneratorColumnIncludeSymbols, crdb.ColumnTypeBool), +func (*secretGeneratorProjection) Name() string { + return SecretGeneratorProjectionTable +} + +func (*secretGeneratorProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(SecretGeneratorColumnGeneratorType, handler.ColumnTypeEnum), + handler.NewColumn(SecretGeneratorColumnAggregateID, handler.ColumnTypeText), + handler.NewColumn(SecretGeneratorColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SecretGeneratorColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SecretGeneratorColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(SecretGeneratorColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(SecretGeneratorColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(SecretGeneratorColumnLength, handler.ColumnTypeInt64), + handler.NewColumn(SecretGeneratorColumnExpiry, handler.ColumnTypeInt64), + handler.NewColumn(SecretGeneratorColumnIncludeLowerLetters, handler.ColumnTypeBool), + handler.NewColumn(SecretGeneratorColumnIncludeUpperLetters, handler.ColumnTypeBool), + handler.NewColumn(SecretGeneratorColumnIncludeDigits, handler.ColumnTypeBool), + handler.NewColumn(SecretGeneratorColumnIncludeSymbols, handler.ColumnTypeBool), }, - crdb.NewPrimaryKey(SecretGeneratorColumnInstanceID, SecretGeneratorColumnGeneratorType, SecretGeneratorColumnAggregateID), + handler.NewPrimaryKey(SecretGeneratorColumnInstanceID, SecretGeneratorColumnGeneratorType, SecretGeneratorColumnAggregateID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *secretGeneratorProjection) reducers() []handler.AggregateReducer { +func (p *secretGeneratorProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.SecretGeneratorAddedEventType, Reduce: p.reduceSecretGeneratorAdded, @@ -90,7 +91,7 @@ func (p *secretGeneratorProjection) reduceSecretGeneratorAdded(event eventstore. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-sk99F", "reduce.wrong.event.type %s", instance.SecretGeneratorAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(SecretGeneratorColumnAggregateID, e.Aggregate().ID), @@ -137,7 +138,7 @@ func (p *secretGeneratorProjection) reduceSecretGeneratorChanged(event eventstor if e.IncludeSymbols != nil { columns = append(columns, handler.NewCol(SecretGeneratorColumnIncludeSymbols, *e.IncludeSymbols)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, columns, []handler.Condition{ @@ -153,7 +154,7 @@ func (p *secretGeneratorProjection) reduceSecretGeneratorRemoved(event eventstor if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-fmiIf", "reduce.wrong.event.type %s", instance.SecretGeneratorRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(SecretGeneratorColumnAggregateID, e.Aggregate().ID), diff --git a/internal/query/projection/secret_generator_test.go b/internal/query/projection/secret_generator_test.go index e0a072d55..4f808d051 100644 --- a/internal/query/projection/secret_generator_test.go +++ b/internal/query/projection/secret_generator_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -25,17 +24,17 @@ func TestSecretGeneratorProjection_reduces(t *testing.T) { { name: "reduceSecretGeneratorRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SecretGeneratorRemovedEventType), - instance.AggregateType, - []byte(`{"generatorType": 1}`), - ), instance.SecretGeneratorRemovedEventMapper), + event: getEvent( + testEvent( + instance.SecretGeneratorRemovedEventType, + instance.AggregateType, + []byte(`{"generatorType": 1}`), + ), instance.SecretGeneratorRemovedEventMapper), }, reduce: (&secretGeneratorProjection{}).reduceSecretGeneratorRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -53,17 +52,17 @@ func TestSecretGeneratorProjection_reduces(t *testing.T) { { name: "reduceSecretGeneratorChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SecretGeneratorChangedEventType), - instance.AggregateType, - []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`), - ), instance.SecretGeneratorChangedEventMapper), + event: getEvent( + testEvent( + instance.SecretGeneratorChangedEventType, + instance.AggregateType, + []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`), + ), instance.SecretGeneratorChangedEventMapper), }, reduce: (&secretGeneratorProjection{}).reduceSecretGeneratorChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -89,17 +88,17 @@ func TestSecretGeneratorProjection_reduces(t *testing.T) { { name: "reduceSecretGeneratorAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SecretGeneratorAddedEventType), - instance.AggregateType, - []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`), - ), instance.SecretGeneratorAddedEventMapper), + event: getEvent( + testEvent( + instance.SecretGeneratorAddedEventType, + instance.AggregateType, + []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`), + ), instance.SecretGeneratorAddedEventMapper), }, reduce: (&secretGeneratorProjection{}).reduceSecretGeneratorAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -127,17 +126,17 @@ func TestSecretGeneratorProjection_reduces(t *testing.T) { { name: "reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(MemberInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/security_policy.go b/internal/query/projection/security_policy.go index 57968aa4b..4c30cfc86 100644 --- a/internal/query/projection/security_policy.go +++ b/internal/query/projection/security_policy.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -20,35 +20,36 @@ const ( SecurityPolicyColumnAllowedOrigins = "origins" ) -type securityPolicyProjection struct { - crdb.StatementHandler +type securityPolicyProjection struct{} + +func newSecurityPolicyProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(securityPolicyProjection)) } -func newSecurityPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *securityPolicyProjection { - p := new(securityPolicyProjection) - config.ProjectionName = SecurityPolicyProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(SecurityPolicyColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SecurityPolicyColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SecurityPolicyColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(SecurityPolicyColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(SecurityPolicyColumnEnabled, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(SecurityPolicyColumnAllowedOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()), +func (*securityPolicyProjection) Name() string { + return SecurityPolicyProjectionTable +} + +func (*securityPolicyProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(SecurityPolicyColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SecurityPolicyColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SecurityPolicyColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(SecurityPolicyColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(SecurityPolicyColumnEnabled, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(SecurityPolicyColumnAllowedOrigins, handler.ColumnTypeTextArray, handler.Nullable()), }, - crdb.NewPrimaryKey(SecurityPolicyColumnInstanceID), + handler.NewPrimaryKey(SecurityPolicyColumnInstanceID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *securityPolicyProjection) reducers() []handler.AggregateReducer { +func (p *securityPolicyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.SecurityPolicySetEventType, Reduce: p.reduceSecurityPolicySet, @@ -79,7 +80,7 @@ func (p *securityPolicyProjection) reduceSecurityPolicySet(event eventstore.Even if e.AllowedOrigins != nil { changes = append(changes, handler.NewCol(SecurityPolicyColumnAllowedOrigins, e.AllowedOrigins)) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( e, []handler.Column{ handler.NewCol(SecurityPolicyColumnInstanceID, ""), diff --git a/internal/query/projection/session.go b/internal/query/projection/session.go index 7305d6a87..816969d66 100644 --- a/internal/query/projection/session.go +++ b/internal/query/projection/session.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/session" "github.com/zitadel/zitadel/internal/repository/user" @@ -37,48 +37,49 @@ const ( SessionColumnTokenID = "token_id" ) -type sessionProjection struct { - crdb.StatementHandler +type sessionProjection struct{} + +func newSessionProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(sessionProjection)) } -func newSessionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *sessionProjection { - p := new(sessionProjection) - config.ProjectionName = SessionsProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(SessionColumnID, crdb.ColumnTypeText), - crdb.NewColumn(SessionColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SessionColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SessionColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(SessionColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(SessionColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(SessionColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(SessionColumnCreator, crdb.ColumnTypeText), - crdb.NewColumn(SessionColumnUserID, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(SessionColumnUserCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnPasswordCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnIntentCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnWebAuthNCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnWebAuthNUserVerified, crdb.ColumnTypeBool, crdb.Nullable()), - crdb.NewColumn(SessionColumnTOTPCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnOTPSMSCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnOTPEmailCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), - crdb.NewColumn(SessionColumnMetadata, crdb.ColumnTypeJSONB, crdb.Nullable()), - crdb.NewColumn(SessionColumnTokenID, crdb.ColumnTypeText, crdb.Nullable()), +func (*sessionProjection) Name() string { + return SessionsProjectionTable +} + +func (*sessionProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(SessionColumnID, handler.ColumnTypeText), + handler.NewColumn(SessionColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SessionColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SessionColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(SessionColumnState, handler.ColumnTypeEnum), + handler.NewColumn(SessionColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(SessionColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(SessionColumnCreator, handler.ColumnTypeText), + handler.NewColumn(SessionColumnUserID, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnPasswordCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnIntentCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnWebAuthNCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnWebAuthNUserVerified, handler.ColumnTypeBool, handler.Nullable()), + handler.NewColumn(SessionColumnTOTPCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnOTPSMSCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnOTPEmailCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(SessionColumnMetadata, handler.ColumnTypeJSONB, handler.Nullable()), + handler.NewColumn(SessionColumnTokenID, handler.ColumnTypeText, handler.Nullable()), }, - crdb.NewPrimaryKey(SessionColumnInstanceID, SessionColumnID), + handler.NewPrimaryKey(SessionColumnInstanceID, SessionColumnID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *sessionProjection) reducers() []handler.AggregateReducer { +func (p *sessionProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: session.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: session.AddedType, Reduce: p.reduceSessionAdded, @@ -127,7 +128,7 @@ func (p *sessionProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(SMSColumnInstanceID), @@ -136,7 +137,7 @@ func (p *sessionProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.HumanPasswordChangedType, Reduce: p.reducePasswordChanged, @@ -152,7 +153,7 @@ func (p *sessionProjection) reduceSessionAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", session.AddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(SessionColumnID, e.Aggregate().ID), @@ -172,7 +173,7 @@ func (p *sessionProjection) reduceUserChecked(event eventstore.Event) (*handler. if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-saDg5", "reduce.wrong.event.type %s", session.UserCheckedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -193,7 +194,7 @@ func (p *sessionProjection) reducePasswordChecked(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDgrb", "reduce.wrong.event.type %s", session.PasswordCheckedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -213,7 +214,7 @@ func (p *sessionProjection) reduceIntentChecked(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDgr2", "reduce.wrong.event.type %s", session.IntentCheckedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -232,7 +233,7 @@ func (p *sessionProjection) reduceWebAuthNChecked(event eventstore.Event) (*hand if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-WieM4", "reduce.wrong.event.type %s", session.WebAuthNCheckedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -253,7 +254,7 @@ func (p *sessionProjection) reduceTOTPChecked(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Oqu8i", "reduce.wrong.event.type %s", session.TOTPCheckedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -273,7 +274,7 @@ func (p *sessionProjection) reduceOTPSMSChecked(event eventstore.Event) (*handle return nil, err } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -293,7 +294,7 @@ func (p *sessionProjection) reduceOTPEmailChecked(event eventstore.Event) (*hand return nil, err } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -313,7 +314,7 @@ func (p *sessionProjection) reduceTokenSet(event eventstore.Event) (*handler.Sta return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAfd3", "reduce.wrong.event.type %s", session.TokenSetType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -333,7 +334,7 @@ func (p *sessionProjection) reduceMetadataSet(event eventstore.Event) (*handler. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAfd3", "reduce.wrong.event.type %s", session.MetadataSetType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), @@ -353,7 +354,7 @@ func (p *sessionProjection) reduceSessionTerminated(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAftn", "reduce.wrong.event.type %s", session.TerminateType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), @@ -368,14 +369,14 @@ func (p *sessionProjection) reducePasswordChanged(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Deg3d", "reduce.wrong.event.type %s", user.HumanPasswordChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnPasswordCheckedAt, nil), }, []handler.Condition{ handler.NewCond(SessionColumnUserID, e.Aggregate().ID), - crdb.NewLessThanCond(SessionColumnPasswordCheckedAt, e.CreationDate()), + handler.NewLessThanCond(SessionColumnPasswordCheckedAt, e.CreationDate()), }, ), nil } diff --git a/internal/query/projection/session_test.go b/internal/query/projection/session_test.go index 8ac52b748..607b028ff 100644 --- a/internal/query/projection/session_test.go +++ b/internal/query/projection/session_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/session" "github.com/zitadel/zitadel/internal/repository/user" @@ -37,9 +36,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceSessionAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -73,9 +71,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceUserChecked, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -106,9 +103,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reducePasswordChecked, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -139,9 +135,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceWebAuthNChecked, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -172,9 +167,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceIntentChecked, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -204,9 +198,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceTOTPChecked, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -236,9 +229,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceTokenSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -270,9 +262,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceMetadataSet, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -302,9 +293,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reduceSessionTerminated, want: wantReduce{ - aggregateType: eventstore.AggregateType("session"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("session"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -321,17 +311,17 @@ func TestSessionProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(SessionColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -348,7 +338,7 @@ func TestSessionProjection_reduces(t *testing.T) { name: "reducePasswordChanged", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanPasswordChangedType), + user.HumanPasswordChangedType, user.AggregateType, []byte(`{"secret": { "cryptoType": 0, @@ -360,9 +350,8 @@ func TestSessionProjection_reduces(t *testing.T) { }, reduce: (&sessionProjection{}).reducePasswordChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/sms.go b/internal/query/projection/sms.go index b2da5df13..eb173a4cf 100644 --- a/internal/query/projection/sms.go +++ b/internal/query/projection/sms.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -32,48 +32,49 @@ const ( SMSTwilioConfigColumnToken = "token" ) -type smsConfigProjection struct { - crdb.StatementHandler +type smsConfigProjection struct{} + +func newSMSConfigProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(smsConfigProjection)) } -func newSMSConfigProjection(ctx context.Context, config crdb.StatementHandlerConfig) *smsConfigProjection { - p := new(smsConfigProjection) - config.ProjectionName = SMSConfigProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(SMSColumnID, crdb.ColumnTypeText), - crdb.NewColumn(SMSColumnAggregateID, crdb.ColumnTypeText), - crdb.NewColumn(SMSColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SMSColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SMSColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(SMSColumnState, crdb.ColumnTypeEnum), - crdb.NewColumn(SMSColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(SMSColumnInstanceID, crdb.ColumnTypeText), +func (*smsConfigProjection) Name() string { + return SMSConfigProjectionTable +} + +func (*smsConfigProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(SMSColumnID, handler.ColumnTypeText), + handler.NewColumn(SMSColumnAggregateID, handler.ColumnTypeText), + handler.NewColumn(SMSColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SMSColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SMSColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(SMSColumnState, handler.ColumnTypeEnum), + handler.NewColumn(SMSColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(SMSColumnInstanceID, handler.ColumnTypeText), }, - crdb.NewPrimaryKey(SMSColumnInstanceID, SMSColumnID), + handler.NewPrimaryKey(SMSColumnInstanceID, SMSColumnID), ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(SMSTwilioConfigColumnSMSID, crdb.ColumnTypeText), - crdb.NewColumn(SMSTwilioColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(SMSTwilioConfigColumnSID, crdb.ColumnTypeText), - crdb.NewColumn(SMSTwilioConfigColumnSenderNumber, crdb.ColumnTypeText), - crdb.NewColumn(SMSTwilioConfigColumnToken, crdb.ColumnTypeJSONB), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(SMSTwilioConfigColumnSMSID, handler.ColumnTypeText), + handler.NewColumn(SMSTwilioColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(SMSTwilioConfigColumnSID, handler.ColumnTypeText), + handler.NewColumn(SMSTwilioConfigColumnSenderNumber, handler.ColumnTypeText), + handler.NewColumn(SMSTwilioConfigColumnToken, handler.ColumnTypeJSONB), }, - crdb.NewPrimaryKey(SMSTwilioColumnInstanceID, SMSTwilioConfigColumnSMSID), + handler.NewPrimaryKey(SMSTwilioColumnInstanceID, SMSTwilioConfigColumnSMSID), smsTwilioTableSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *smsConfigProjection) reducers() []handler.AggregateReducer { +func (p *smsConfigProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.SMSConfigTwilioAddedEventType, Reduce: p.reduceSMSConfigTwilioAdded, @@ -113,9 +114,9 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-s8efs", "reduce.wrong.event.type %s", instance.SMSConfigTwilioAddedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(SMSColumnID, e.ID), handler.NewCol(SMSColumnAggregateID, e.Aggregate().ID), @@ -127,7 +128,7 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) handler.NewCol(SMSColumnSequence, e.Sequence()), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(SMSTwilioConfigColumnSMSID, e.ID), handler.NewCol(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), @@ -135,7 +136,7 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) handler.NewCol(SMSTwilioConfigColumnToken, e.Token), handler.NewCol(SMSTwilioConfigColumnSenderNumber, e.SenderNumber), }, - crdb.WithTableSuffix(smsTwilioTableSuffix), + handler.WithTableSuffix(smsTwilioTableSuffix), ), ), nil } @@ -153,17 +154,17 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioChanged(event eventstore.Even columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSenderNumber, *e.SenderNumber)) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( columns, []handler.Condition{ handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID), handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(smsTwilioTableSuffix), + handler.WithTableSuffix(smsTwilioTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(SMSColumnChangeDate, e.CreationDate()), handler.NewCol(SMSColumnSequence, e.Sequence()), @@ -186,17 +187,17 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioTokenChanged(event eventstore columns = append(columns, handler.NewCol(SMSTwilioConfigColumnToken, e.Token)) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( columns, []handler.Condition{ handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID), handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(smsTwilioTableSuffix), + handler.WithTableSuffix(smsTwilioTableSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(SMSColumnChangeDate, e.CreationDate()), handler.NewCol(SMSColumnSequence, e.Sequence()), @@ -214,7 +215,7 @@ func (p *smsConfigProjection) reduceSMSConfigActivated(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-fj9Ef", "reduce.wrong.event.type %s", instance.SMSConfigActivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SMSColumnState, domain.SMSConfigStateActive), @@ -233,7 +234,7 @@ func (p *smsConfigProjection) reduceSMSConfigDeactivated(event eventstore.Event) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-dj9Js", "reduce.wrong.event.type %s", instance.SMSConfigDeactivatedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive), @@ -252,7 +253,7 @@ func (p *smsConfigProjection) reduceSMSConfigRemoved(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-s9JJf", "reduce.wrong.event.type %s", instance.SMSConfigRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(SMSColumnID, e.ID), diff --git a/internal/query/projection/sms_test.go b/internal/query/projection/sms_test.go index 17ce2bec3..c3ca8f99f 100644 --- a/internal/query/projection/sms_test.go +++ b/internal/query/projection/sms_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -25,10 +24,11 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSTwilioAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigTwilioAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigTwilioAddedEventType, + instance.AggregateType, + []byte(`{ "id": "id", "sid": "sid", "token": { @@ -39,13 +39,12 @@ func TestSMSProjection_reduces(t *testing.T) { }, "senderNumber": "sender-number" }`), - ), instance.SMSConfigTwilioAddedEventMapper), + ), instance.SMSConfigTwilioAddedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -83,21 +82,21 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSConfigTwilioChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigTwilioChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigTwilioChangedEventType, + instance.AggregateType, + []byte(`{ "id": "id", "sid": "sid", "senderNumber": "sender-number" }`), - ), instance.SMSConfigTwilioChangedEventMapper), + ), instance.SMSConfigTwilioChangedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -125,10 +124,11 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSConfigTwilioTokenChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigTwilioTokenChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigTwilioTokenChangedEventType, + instance.AggregateType, + []byte(`{ "id": "id", "token": { "cryptoType": 0, @@ -137,13 +137,12 @@ func TestSMSProjection_reduces(t *testing.T) { "crypted": "Y3J5cHRlZA==" } }`), - ), instance.SMSConfigTwilioTokenChangedEventMapper), + ), instance.SMSConfigTwilioTokenChangedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioTokenChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -175,19 +174,19 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSConfigActivated", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigActivatedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigActivatedEventType, + instance.AggregateType, + []byte(`{ "id": "id" }`), - ), instance.SMSConfigActivatedEventMapper), + ), instance.SMSConfigActivatedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigActivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -207,19 +206,19 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSConfigDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigDeactivatedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigDeactivatedEventType, + instance.AggregateType, + []byte(`{ "id": "id" }`), - ), instance.SMSConfigDeactivatedEventMapper), + ), instance.SMSConfigDeactivatedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigDeactivated, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -239,19 +238,19 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceSMSConfigRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMSConfigRemovedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMSConfigRemovedEventType, + instance.AggregateType, + []byte(`{ "id": "id" }`), - ), instance.SMSConfigRemovedEventMapper), + ), instance.SMSConfigRemovedEventMapper), }, reduce: (&smsConfigProjection{}).reduceSMSConfigRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -268,17 +267,17 @@ func TestSMSProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(SMSColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/smtp.go b/internal/query/projection/smtp.go index c8c96e871..40f151950 100644 --- a/internal/query/projection/smtp.go +++ b/internal/query/projection/smtp.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -28,42 +28,43 @@ const ( SMTPConfigColumnSMTPPassword = "password" ) -type smtpConfigProjection struct { - crdb.StatementHandler +type smtpConfigProjection struct{} + +func newSMTPConfigProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(smtpConfigProjection)) } -func newSMTPConfigProjection(ctx context.Context, config crdb.StatementHandlerConfig) *smtpConfigProjection { - p := new(smtpConfigProjection) - config.ProjectionName = SMTPConfigProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(SMTPConfigColumnAggregateID, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SMTPConfigColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(SMTPConfigColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(SMTPConfigColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnTLS, crdb.ColumnTypeBool), - crdb.NewColumn(SMTPConfigColumnSenderAddress, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnSenderName, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnReplyToAddress, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnSMTPHost, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnSMTPUser, crdb.ColumnTypeText), - crdb.NewColumn(SMTPConfigColumnSMTPPassword, crdb.ColumnTypeJSONB, crdb.Nullable()), +func (*smtpConfigProjection) Name() string { + return SMTPConfigProjectionTable +} + +func (*smtpConfigProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(SMTPConfigColumnAggregateID, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SMTPConfigColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(SMTPConfigColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(SMTPConfigColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnTLS, handler.ColumnTypeBool), + handler.NewColumn(SMTPConfigColumnSenderAddress, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnSenderName, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnReplyToAddress, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnSMTPHost, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnSMTPUser, handler.ColumnTypeText), + handler.NewColumn(SMTPConfigColumnSMTPPassword, handler.ColumnTypeJSONB, handler.Nullable()), }, - crdb.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnAggregateID), + handler.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnAggregateID), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *smtpConfigProjection) reducers() []handler.AggregateReducer { +func (p *smtpConfigProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.SMTPConfigAddedEventType, Reduce: p.reduceSMTPConfigAdded, @@ -94,7 +95,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-sk99F", "reduce.wrong.event.type %s", instance.SMTPConfigAddedEventType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(SMTPConfigColumnAggregateID, e.Aggregate().ID), @@ -141,7 +142,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) ( if e.User != nil { columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPUser, *e.User)) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, columns, []handler.Condition{ @@ -157,7 +158,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore. return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-fk02f", "reduce.wrong.event.type %s", instance.SMTPConfigChangedEventType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()), @@ -176,7 +177,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigRemoved(event eventstore.Event) ( if err != nil { return nil, err } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID), diff --git a/internal/query/projection/smtp_test.go b/internal/query/projection/smtp_test.go index 9c7efb432..922ef1247 100644 --- a/internal/query/projection/smtp_test.go +++ b/internal/query/projection/smtp_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" ) @@ -23,10 +22,11 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { { name: "reduceSMTPConfigChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMTPConfigChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMTPConfigChangedEventType, + instance.AggregateType, + []byte(`{ "tls": true, "senderAddress": "sender", "senderName": "name", @@ -34,14 +34,13 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { "host": "host", "user": "user" }`, - ), - ), instance.SMTPConfigChangedEventMapper), + ), + ), instance.SMTPConfigChangedEventMapper), }, reduce: (&smtpConfigProjection{}).reduceSMTPConfigChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -66,10 +65,11 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { { name: "reduceSMTPConfigAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMTPConfigAddedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMTPConfigAddedEventType, + instance.AggregateType, + []byte(`{ "tls": true, "senderAddress": "sender", "senderName": "name", @@ -82,13 +82,12 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { "keyId": "key-id" } }`), - ), instance.SMTPConfigAddedEventMapper), + ), instance.SMTPConfigAddedEventMapper), }, reduce: (&smtpConfigProjection{}).reduceSMTPConfigAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -116,23 +115,23 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { { name: "reduceSMTPConfigPasswordChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.SMTPConfigPasswordChangedEventType), - instance.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + instance.SMTPConfigPasswordChangedEventType, + instance.AggregateType, + []byte(`{ "password": { "cryptoType": 0, "algorithm": "RSA-265", "keyId": "key-id" } }`), - ), instance.SMTPConfigPasswordChangedEventMapper), + ), instance.SMTPConfigPasswordChangedEventMapper), }, reduce: (&smtpConfigProjection{}).reduceSMTPConfigPasswordChanged, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -153,16 +152,15 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { name: "reduceSMTPConfigRemoved", args: args{ event: getEvent(testEvent( - repository.EventType(instance.SMTPConfigRemovedEventType), + instance.SMTPConfigRemovedEventType, instance.AggregateType, []byte(`{}`), ), instance.SMTPConfigRemovedEventMapper), }, reduce: (&smtpConfigProjection{}).reduceSMTPConfigRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -179,17 +177,17 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(SMTPConfigColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/user.go b/internal/query/projection/user.go index a7ad4a345..cafbbfee7 100644 --- a/internal/query/projection/user.go +++ b/internal/query/projection/user.go @@ -7,17 +7,13 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" ) -type userProjection struct { - crdb.StatementHandler -} - const ( UserTable = "projections.users8" UserHumanTable = UserTable + "_" + UserHumanSuffix @@ -76,82 +72,87 @@ const ( NotifyPasswordSetCol = "password_set" ) -func newUserProjection(ctx context.Context, config crdb.StatementHandlerConfig) *userProjection { - p := new(userProjection) - config.ProjectionName = UserTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(UserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(UserCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(UserStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(UserResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(UserInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(UserUsernameCol, crdb.ColumnTypeText), - crdb.NewColumn(UserTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(UserOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), - }, - crdb.NewPrimaryKey(UserInstanceIDCol, UserIDCol), - crdb.WithIndex(crdb.NewIndex("username", []string{UserUsernameCol})), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{UserResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{UserOwnerRemovedCol})), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(HumanUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(HumanUserInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(HumanFirstNameCol, crdb.ColumnTypeText), - crdb.NewColumn(HumanLastNameCol, crdb.ColumnTypeText), - crdb.NewColumn(HumanNickNameCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(HumanDisplayNameCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(HumanPreferredLanguageCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(HumanGenderCol, crdb.ColumnTypeEnum, crdb.Nullable()), - crdb.NewColumn(HumanAvatarURLCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(HumanEmailCol, crdb.ColumnTypeText), - crdb.NewColumn(HumanIsEmailVerifiedCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(HumanPhoneCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(HumanIsPhoneVerifiedCol, crdb.ColumnTypeBool, crdb.Nullable()), - }, - crdb.NewPrimaryKey(HumanUserInstanceIDCol, HumanUserIDCol), - UserHumanSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(MachineUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MachineUserInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(MachineNameCol, crdb.ColumnTypeText), - crdb.NewColumn(MachineDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(MachineHasSecretCol, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(MachineAccessTokenTypeCol, crdb.ColumnTypeEnum, crdb.Default(0)), - }, - crdb.NewPrimaryKey(MachineUserInstanceIDCol, MachineUserIDCol), - UserMachineSuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(NotifyUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(NotifyInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(NotifyLastEmailCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(NotifyVerifiedEmailCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(NotifyLastPhoneCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(NotifyVerifiedPhoneCol, crdb.ColumnTypeText, crdb.Nullable()), - crdb.NewColumn(NotifyPasswordSetCol, crdb.ColumnTypeBool, crdb.Default(false)), - }, - crdb.NewPrimaryKey(NotifyInstanceIDCol, NotifyUserIDCol), - UserNotifySuffix, - crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), - ), - ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p +type userProjection struct{} + +func newUserProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(userProjection)) } -func (p *userProjection) reducers() []handler.AggregateReducer { +func (*userProjection) Name() string { + return UserTable +} + +func (*userProjection) Init() *old_handler.Check { + return handler.NewMultiTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(UserIDCol, handler.ColumnTypeText), + handler.NewColumn(UserCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(UserChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(UserSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(UserStateCol, handler.ColumnTypeEnum), + handler.NewColumn(UserResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(UserInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(UserUsernameCol, handler.ColumnTypeText), + handler.NewColumn(UserTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(UserOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), + }, + handler.NewPrimaryKey(UserInstanceIDCol, UserIDCol), + handler.WithIndex(handler.NewIndex("username", []string{UserUsernameCol})), + handler.WithIndex(handler.NewIndex("resource_owner", []string{UserResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{UserOwnerRemovedCol})), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(HumanUserIDCol, handler.ColumnTypeText), + handler.NewColumn(HumanUserInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(HumanFirstNameCol, handler.ColumnTypeText), + handler.NewColumn(HumanLastNameCol, handler.ColumnTypeText), + handler.NewColumn(HumanNickNameCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(HumanDisplayNameCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(HumanPreferredLanguageCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(HumanGenderCol, handler.ColumnTypeEnum, handler.Nullable()), + handler.NewColumn(HumanAvatarURLCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(HumanEmailCol, handler.ColumnTypeText), + handler.NewColumn(HumanIsEmailVerifiedCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(HumanPhoneCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(HumanIsPhoneVerifiedCol, handler.ColumnTypeBool, handler.Nullable()), + }, + handler.NewPrimaryKey(HumanUserInstanceIDCol, HumanUserIDCol), + UserHumanSuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(MachineUserIDCol, handler.ColumnTypeText), + handler.NewColumn(MachineUserInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(MachineNameCol, handler.ColumnTypeText), + handler.NewColumn(MachineDescriptionCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(MachineHasSecretCol, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(MachineAccessTokenTypeCol, handler.ColumnTypeEnum, handler.Default(0)), + }, + handler.NewPrimaryKey(MachineUserInstanceIDCol, MachineUserIDCol), + UserMachineSuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + handler.NewSuffixedTable([]*handler.InitColumn{ + handler.NewColumn(NotifyUserIDCol, handler.ColumnTypeText), + handler.NewColumn(NotifyInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(NotifyLastEmailCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(NotifyVerifiedEmailCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(NotifyLastPhoneCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(NotifyVerifiedPhoneCol, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(NotifyPasswordSetCol, handler.ColumnTypeBool, handler.Default(false)), + }, + handler.NewPrimaryKey(NotifyInstanceIDCol, NotifyUserIDCol), + UserNotifySuffix, + handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), + ), + ) +} + +func (p *userProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserV1AddedType, Reduce: p.reduceHumanAdded, @@ -292,7 +293,7 @@ func (p *userProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -301,7 +302,7 @@ func (p *userProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(UserInstanceIDCol), @@ -316,9 +317,9 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Ebynp", "reduce.wrong.event.type %s", user.HumanAddedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(UserIDCol, e.Aggregate().ID), handler.NewCol(UserCreationDateCol, e.CreationDate()), @@ -331,7 +332,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat handler.NewCol(UserTypeCol, domain.UserTypeHuman), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(HumanUserIDCol, e.Aggregate().ID), handler.NewCol(HumanUserInstanceIDCol, e.Aggregate().InstanceID), @@ -344,9 +345,9 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat handler.NewCol(HumanEmailCol, e.EmailAddress), handler.NewCol(HumanPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(NotifyUserIDCol, e.Aggregate().ID), handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID), @@ -354,7 +355,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -364,9 +365,9 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-xE53M", "reduce.wrong.event.type %s", user.HumanRegisteredType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(UserIDCol, e.Aggregate().ID), handler.NewCol(UserCreationDateCol, e.CreationDate()), @@ -379,7 +380,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler handler.NewCol(UserTypeCol, domain.UserTypeHuman), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(HumanUserIDCol, e.Aggregate().ID), handler.NewCol(HumanUserInstanceIDCol, e.Aggregate().InstanceID), @@ -392,9 +393,9 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler handler.NewCol(HumanEmailCol, e.EmailAddress), handler.NewCol(HumanPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(NotifyUserIDCol, e.Aggregate().ID), handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID), @@ -402,7 +403,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -412,7 +413,7 @@ func (p *userProjection) reduceHumanInitCodeAdded(event eventstore.Event) (*hand if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dvgws", "reduce.wrong.event.type %s", user.HumanInitialCodeAddedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserStateCol, domain.UserStateInitial), @@ -429,7 +430,7 @@ func (p *userProjection) reduceHumanInitCodeSucceeded(event eventstore.Event) (* if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dfvwq", "reduce.wrong.event.type %s", user.HumanInitializedCheckSucceededType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserStateCol, domain.UserStateActive), @@ -447,7 +448,7 @@ func (p *userProjection) reduceUserLocked(event eventstore.Event) (*handler.Stat return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-exyBF", "reduce.wrong.event.type %s", user.UserLockedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -467,7 +468,7 @@ func (p *userProjection) reduceUserUnlocked(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-JIyRl", "reduce.wrong.event.type %s", user.UserUnlockedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -487,7 +488,7 @@ func (p *userProjection) reduceUserDeactivated(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-6BNjj", "reduce.wrong.event.type %s", user.UserDeactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -507,7 +508,7 @@ func (p *userProjection) reduceUserReactivated(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-IoF6j", "reduce.wrong.event.type %s", user.UserReactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -527,7 +528,7 @@ func (p *userProjection) reduceUserRemoved(event eventstore.Event) (*handler.Sta return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-BQB2t", "reduce.wrong.event.type %s", user.UserRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), @@ -542,7 +543,7 @@ func (p *userProjection) reduceUserNameChanged(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-QNKyV", "reduce.wrong.event.type %s", user.UserUserNameChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -562,7 +563,7 @@ func (p *userProjection) reduceDomainClaimed(event eventstore.Event) (*handler.S return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASwf3", "reduce.wrong.event.type %s", user.UserDomainClaimedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), @@ -606,9 +607,9 @@ func (p *userProjection) reduceHumanProfileChanged(event eventstore.Event) (*han cols = append(cols, handler.NewCol(HumanGenderCol, *e.Gender)) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -618,13 +619,13 @@ func (p *userProjection) reduceHumanProfileChanged(event eventstore.Event) (*han handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), ), nil } @@ -635,9 +636,9 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-xOGIA", "reduce.wrong.event.type %s", user.HumanPhoneChangedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -647,7 +648,7 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanPhoneCol, e.PhoneNumber), handler.NewCol(HumanIsPhoneVerifiedCol, false), @@ -656,9 +657,9 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}), }, @@ -666,7 +667,7 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -677,9 +678,9 @@ func (p *userProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-JI4S1", "reduce.wrong.event.type %s", user.HumanPhoneRemovedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -689,7 +690,7 @@ func (p *userProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanPhoneCol, nil), handler.NewCol(HumanIsPhoneVerifiedCol, nil), @@ -698,9 +699,9 @@ func (p *userProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(NotifyLastPhoneCol, nil), handler.NewCol(NotifyVerifiedPhoneCol, nil), @@ -709,7 +710,7 @@ func (p *userProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -720,9 +721,9 @@ func (p *userProjection) reduceHumanPhoneVerified(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-LBnqG", "reduce.wrong.event.type %s", user.HumanPhoneVerifiedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -732,7 +733,7 @@ func (p *userProjection) reduceHumanPhoneVerified(event eventstore.Event) (*hand handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanIsPhoneVerifiedCol, true), }, @@ -740,17 +741,17 @@ func (p *userProjection) reduceHumanPhoneVerified(event eventstore.Event) (*hand handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ - crdb.NewCopyCol(NotifyVerifiedPhoneCol, NotifyLastPhoneCol), + handler.NewCopyCol(NotifyVerifiedPhoneCol, NotifyLastPhoneCol), }, []handler.Condition{ handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -761,9 +762,9 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-KwiHa", "reduce.wrong.event.type %s", user.HumanEmailChangedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -773,7 +774,7 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanEmailCol, e.EmailAddress), handler.NewCol(HumanIsEmailVerifiedCol, false), @@ -782,9 +783,9 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(NotifyLastEmailCol, &sql.NullString{String: string(e.EmailAddress), Valid: e.EmailAddress != ""}), }, @@ -792,7 +793,7 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -803,9 +804,9 @@ func (p *userProjection) reduceHumanEmailVerified(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-JzcDq", "reduce.wrong.event.type %s", user.HumanEmailVerifiedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -815,7 +816,7 @@ func (p *userProjection) reduceHumanEmailVerified(event eventstore.Event) (*hand handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanIsEmailVerifiedCol, true), }, @@ -823,17 +824,17 @@ func (p *userProjection) reduceHumanEmailVerified(event eventstore.Event) (*hand handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ - crdb.NewCopyCol(NotifyVerifiedEmailCol, NotifyLastEmailCol), + handler.NewCopyCol(NotifyVerifiedEmailCol, NotifyLastEmailCol), }, []handler.Condition{ handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), ), nil } @@ -844,9 +845,9 @@ func (p *userProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-eDEdt", "reduce.wrong.event.type %s", user.HumanAvatarAddedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -856,7 +857,7 @@ func (p *userProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handle handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanAvatarURLCol, e.StoreKey), }, @@ -864,7 +865,7 @@ func (p *userProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handle handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), ), nil } @@ -875,9 +876,9 @@ func (p *userProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*hand return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-KhETX", "reduce.wrong.event.type %s", user.HumanAvatarRemovedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -887,7 +888,7 @@ func (p *userProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*hand handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(HumanAvatarURLCol, nil), }, @@ -895,7 +896,7 @@ func (p *userProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*hand handler.NewCond(HumanUserIDCol, e.Aggregate().ID), handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserHumanSuffix), + handler.WithTableSuffix(UserHumanSuffix), ), ), nil } @@ -906,7 +907,7 @@ func (p *userProjection) reduceHumanPasswordChanged(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-jqXUY", "reduce.wrong.event.type %s", user.HumanPasswordChangedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( e, []handler.Column{ handler.NewCol(NotifyPasswordSetCol, true), @@ -915,7 +916,7 @@ func (p *userProjection) reduceHumanPasswordChanged(event eventstore.Event) (*ha handler.NewCond(NotifyUserIDCol, e.Aggregate().ID), handler.NewCond(NotifyInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserNotifySuffix), + handler.WithTableSuffix(UserNotifySuffix), ), nil } @@ -924,9 +925,9 @@ func (p *userProjection) reduceMachineSecretSet(event eventstore.Event) (*handle if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x0p1n1i", "reduce.wrong.event.type %s", user.MachineSecretSetType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -936,7 +937,7 @@ func (p *userProjection) reduceMachineSecretSet(event eventstore.Event) (*handle handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(MachineHasSecretCol, true), }, @@ -944,7 +945,7 @@ func (p *userProjection) reduceMachineSecretSet(event eventstore.Event) (*handle handler.NewCond(MachineUserIDCol, e.Aggregate().ID), handler.NewCond(MachineUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserMachineSuffix), + handler.WithTableSuffix(UserMachineSuffix), ), ), nil } @@ -955,9 +956,9 @@ func (p *userProjection) reduceMachineSecretRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x0p6n1i", "reduce.wrong.event.type %s", user.MachineSecretRemovedType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -967,7 +968,7 @@ func (p *userProjection) reduceMachineSecretRemoved(event eventstore.Event) (*ha handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(MachineHasSecretCol, false), }, @@ -975,7 +976,7 @@ func (p *userProjection) reduceMachineSecretRemoved(event eventstore.Event) (*ha handler.NewCond(MachineUserIDCol, e.Aggregate().ID), handler.NewCond(MachineUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserMachineSuffix), + handler.WithTableSuffix(UserMachineSuffix), ), ), nil } @@ -986,9 +987,9 @@ func (p *userProjection) reduceMachineAdded(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-q7ier", "reduce.wrong.event.type %s", user.MachineAddedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(UserIDCol, e.Aggregate().ID), handler.NewCol(UserCreationDateCol, e.CreationDate()), @@ -1001,7 +1002,7 @@ func (p *userProjection) reduceMachineAdded(event eventstore.Event) (*handler.St handler.NewCol(UserTypeCol, domain.UserTypeMachine), }, ), - crdb.AddCreateStatement( + handler.AddCreateStatement( []handler.Column{ handler.NewCol(MachineUserIDCol, e.Aggregate().ID), handler.NewCol(MachineUserInstanceIDCol, e.Aggregate().InstanceID), @@ -1009,7 +1010,7 @@ func (p *userProjection) reduceMachineAdded(event eventstore.Event) (*handler.St handler.NewCol(MachineDescriptionCol, &sql.NullString{String: e.Description, Valid: e.Description != ""}), handler.NewCol(MachineAccessTokenTypeCol, e.AccessTokenType), }, - crdb.WithTableSuffix(UserMachineSuffix), + handler.WithTableSuffix(UserMachineSuffix), ), ), nil } @@ -1031,12 +1032,12 @@ func (p *userProjection) reduceMachineChanged(event eventstore.Event) (*handler. cols = append(cols, handler.NewCol(MachineAccessTokenTypeCol, e.AccessTokenType)) } if len(cols) == 0 { - return crdb.NewNoOpStatement(e), nil + return handler.NewNoOpStatement(e), nil } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddUpdateStatement( + handler.AddUpdateStatement( []handler.Column{ handler.NewCol(UserChangeDateCol, e.CreationDate()), handler.NewCol(UserSequenceCol, e.Sequence()), @@ -1046,13 +1047,13 @@ func (p *userProjection) reduceMachineChanged(event eventstore.Event) (*handler. handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), - crdb.AddUpdateStatement( + handler.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(MachineUserIDCol, e.Aggregate().ID), handler.NewCond(MachineUserInstanceIDCol, e.Aggregate().InstanceID), }, - crdb.WithTableSuffix(UserMachineSuffix), + handler.WithTableSuffix(UserMachineSuffix), ), ), nil @@ -1064,7 +1065,7 @@ func (p *userProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-NCsdV", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/user_auth_method.go b/internal/query/projection/user_auth_method.go index 7e40fcb90..49455c81b 100644 --- a/internal/query/projection/user_auth_method.go +++ b/internal/query/projection/user_auth_method.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -29,42 +29,43 @@ const ( UserAuthMethodOwnerRemovedCol = "owner_removed" ) -type userAuthMethodProjection struct { - crdb.StatementHandler +type userAuthMethodProjection struct{} + +func newUserAuthMethodProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(userAuthMethodProjection)) } -func newUserAuthMethodProjection(ctx context.Context, config crdb.StatementHandlerConfig) *userAuthMethodProjection { - p := new(userAuthMethodProjection) - config.ProjectionName = UserAuthMethodTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(UserAuthMethodUserIDCol, crdb.ColumnTypeText), - crdb.NewColumn(UserAuthMethodTypeCol, crdb.ColumnTypeEnum), - crdb.NewColumn(UserAuthMethodTokenIDCol, crdb.ColumnTypeText), - crdb.NewColumn(UserAuthMethodCreationDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserAuthMethodChangeDateCol, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserAuthMethodSequenceCol, crdb.ColumnTypeInt64), - crdb.NewColumn(UserAuthMethodStateCol, crdb.ColumnTypeEnum), - crdb.NewColumn(UserAuthMethodResourceOwnerCol, crdb.ColumnTypeText), - crdb.NewColumn(UserAuthMethodInstanceIDCol, crdb.ColumnTypeText), - crdb.NewColumn(UserAuthMethodNameCol, crdb.ColumnTypeText), - crdb.NewColumn(UserAuthMethodOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)), +func (*userAuthMethodProjection) Name() string { + return UserAuthMethodTable +} + +func (*userAuthMethodProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(UserAuthMethodUserIDCol, handler.ColumnTypeText), + handler.NewColumn(UserAuthMethodTypeCol, handler.ColumnTypeEnum), + handler.NewColumn(UserAuthMethodTokenIDCol, handler.ColumnTypeText), + handler.NewColumn(UserAuthMethodCreationDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(UserAuthMethodChangeDateCol, handler.ColumnTypeTimestamp), + handler.NewColumn(UserAuthMethodSequenceCol, handler.ColumnTypeInt64), + handler.NewColumn(UserAuthMethodStateCol, handler.ColumnTypeEnum), + handler.NewColumn(UserAuthMethodResourceOwnerCol, handler.ColumnTypeText), + handler.NewColumn(UserAuthMethodInstanceIDCol, handler.ColumnTypeText), + handler.NewColumn(UserAuthMethodNameCol, handler.ColumnTypeText), + handler.NewColumn(UserAuthMethodOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(UserAuthMethodInstanceIDCol, UserAuthMethodUserIDCol, UserAuthMethodTypeCol, UserAuthMethodTokenIDCol), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{UserAuthMethodResourceOwnerCol})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{UserAuthMethodOwnerRemovedCol})), + handler.NewPrimaryKey(UserAuthMethodInstanceIDCol, UserAuthMethodUserIDCol, UserAuthMethodTypeCol, UserAuthMethodTokenIDCol), + handler.WithIndex(handler.NewIndex("resource_owner", []string{UserAuthMethodResourceOwnerCol})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{UserAuthMethodOwnerRemovedCol})), ), ) - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *userAuthMethodProjection) reducers() []handler.AggregateReducer { +func (p *userAuthMethodProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.HumanPasswordlessTokenAddedType, Reduce: p.reduceInitAuthMethod, @@ -129,7 +130,7 @@ func (p *userAuthMethodProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -138,7 +139,7 @@ func (p *userAuthMethodProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(UserAuthMethodInstanceIDCol), @@ -164,7 +165,7 @@ func (p *userAuthMethodProjection) reduceInitAuthMethod(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-f92f", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanPasswordlessTokenAddedType, user.HumanU2FTokenAddedType}) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( event, []handler.Column{ handler.NewCol(UserAuthMethodInstanceIDCol, nil), @@ -174,8 +175,8 @@ func (p *userAuthMethodProjection) reduceInitAuthMethod(event eventstore.Event) }, []handler.Column{ handler.NewCol(UserAuthMethodTokenIDCol, tokenID), - handler.NewCol(UserAuthMethodCreationDateCol, event.CreationDate()), - handler.NewCol(UserAuthMethodChangeDateCol, event.CreationDate()), + handler.NewCol(UserAuthMethodCreationDateCol, event.CreatedAt()), + handler.NewCol(UserAuthMethodChangeDateCol, event.CreatedAt()), handler.NewCol(UserAuthMethodResourceOwnerCol, event.Aggregate().ResourceOwner), handler.NewCol(UserAuthMethodInstanceIDCol, event.Aggregate().InstanceID), handler.NewCol(UserAuthMethodUserIDCol, event.Aggregate().ID), @@ -208,10 +209,10 @@ func (p *userAuthMethodProjection) reduceActivateEvent(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-f92f", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanPasswordlessTokenAddedType, user.HumanU2FTokenAddedType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(UserAuthMethodChangeDateCol, event.CreationDate()), + handler.NewCol(UserAuthMethodChangeDateCol, event.CreatedAt()), handler.NewCol(UserAuthMethodSequenceCol, event.Sequence()), handler.NewCol(UserAuthMethodNameCol, name), handler.NewCol(UserAuthMethodStateCol, domain.MFAStateReady), @@ -237,12 +238,12 @@ func (p *userAuthMethodProjection) reduceAddAuthMethod(event eventstore.Event) ( return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-DS4g3", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanOTPSMSAddedType, user.HumanOTPEmailAddedType}) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( event, []handler.Column{ handler.NewCol(UserAuthMethodTokenIDCol, ""), - handler.NewCol(UserAuthMethodCreationDateCol, event.CreationDate()), - handler.NewCol(UserAuthMethodChangeDateCol, event.CreationDate()), + handler.NewCol(UserAuthMethodCreationDateCol, event.CreatedAt()), + handler.NewCol(UserAuthMethodChangeDateCol, event.CreatedAt()), handler.NewCol(UserAuthMethodResourceOwnerCol, event.Aggregate().ResourceOwner), handler.NewCol(UserAuthMethodInstanceIDCol, event.Aggregate().InstanceID), handler.NewCol(UserAuthMethodUserIDCol, event.Aggregate().ID), @@ -286,7 +287,7 @@ func (p *userAuthMethodProjection) reduceRemoveAuthMethod(event eventstore.Event if tokenID != "" { conditions = append(conditions, handler.NewCond(UserAuthMethodTokenIDCol, tokenID)) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, conditions, ), nil @@ -298,7 +299,7 @@ func (p *userAuthMethodProjection) reduceOwnerRemoved(event eventstore.Event) (* return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-FwDZ8", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(UserAuthMethodInstanceIDCol, e.Aggregate().InstanceID), diff --git a/internal/query/projection/user_auth_method_test.go b/internal/query/projection/user_auth_method_test.go index 6808d62af..e3d52f253 100644 --- a/internal/query/projection/user_auth_method_test.go +++ b/internal/query/projection/user_auth_method_test.go @@ -6,8 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -26,19 +25,19 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceAddedPasswordless", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanPasswordlessTokenAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanPasswordlessTokenAddedType, + user.AggregateType, + []byte(`{ "webAuthNTokenId": "token-id" }`), - ), user.HumanPasswordlessAddedEventMapper), + ), user.HumanPasswordlessAddedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceInitAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -63,19 +62,19 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceAddedU2F", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanU2FTokenAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanU2FTokenAddedType, + user.AggregateType, + []byte(`{ "webAuthNTokenId": "token-id" }`), - ), user.HumanU2FAddedEventMapper), + ), user.HumanU2FAddedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceInitAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -100,18 +99,18 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceAddedTOTP", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanMFAOTPAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanMFAOTPAddedType, + user.AggregateType, + []byte(`{ }`), - ), user.HumanOTPAddedEventMapper), + ), user.HumanOTPAddedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceInitAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -136,20 +135,20 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceVerifiedPasswordless", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanPasswordlessTokenVerifiedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanPasswordlessTokenVerifiedType, + user.AggregateType, + []byte(`{ "webAuthNTokenId": "token-id", "webAuthNTokenName": "name" }`), - ), user.HumanPasswordlessVerifiedEventMapper), + ), user.HumanPasswordlessVerifiedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceActivateEvent, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -173,20 +172,20 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceVerifiedU2F", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanU2FTokenVerifiedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanU2FTokenVerifiedType, + user.AggregateType, + []byte(`{ "webAuthNTokenId": "token-id", "webAuthNTokenName": "name" }`), - ), user.HumanU2FVerifiedEventMapper), + ), user.HumanU2FVerifiedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceActivateEvent, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -210,18 +209,18 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "reduceVerifiedTOTP", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanMFAOTPVerifiedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanMFAOTPVerifiedType, + user.AggregateType, + []byte(`{ }`), - ), user.HumanOTPVerifiedEventMapper), + ), user.HumanOTPVerifiedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceActivateEvent, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -246,16 +245,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceAddedOTPSMS", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanOTPSMSAddedType), + user.HumanOTPSMSAddedType, user.AggregateType, nil, ), eventstore.GenericEventMapper[user.HumanOTPSMSAddedEvent]), }, reduce: (&userAuthMethodProjection{}).reduceAddAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -281,16 +279,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceAddedOTPEmail", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanOTPEmailAddedType), + user.HumanOTPEmailAddedType, user.AggregateType, nil, ), eventstore.GenericEventMapper[user.HumanOTPEmailAddedEvent]), }, reduce: (&userAuthMethodProjection{}).reduceAddAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -316,7 +313,7 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemoveOTPPasswordless", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanPasswordlessTokenRemovedType), + user.HumanPasswordlessTokenRemovedType, user.AggregateType, []byte(`{ "webAuthNTokenId": "token-id" @@ -325,9 +322,8 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -348,7 +344,7 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemoveOTPU2F", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanU2FTokenRemovedType), + user.HumanU2FTokenRemovedType, user.AggregateType, []byte(`{ "webAuthNTokenId": "token-id" @@ -357,9 +353,8 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -380,16 +375,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemoveTOTP", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanMFAOTPRemovedType), + user.HumanMFAOTPRemovedType, user.AggregateType, nil, ), user.HumanOTPRemovedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -409,16 +403,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemoveOTPSMS", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanOTPSMSRemovedType), + user.HumanOTPSMSRemovedType, user.AggregateType, nil, ), eventstore.GenericEventMapper[user.HumanOTPSMSRemovedEvent]), }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -438,16 +431,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemovePhone", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanPhoneRemovedType), + user.HumanPhoneRemovedType, user.AggregateType, nil, ), user.HumanPhoneRemovedEventMapper), }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -467,16 +459,15 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "reduceRemoveOTPEmail", args: args{ event: getEvent(testEvent( - repository.EventType(user.HumanOTPEmailRemovedType), + user.HumanOTPEmailRemovedType, user.AggregateType, nil, ), eventstore.GenericEventMapper[user.HumanOTPEmailRemovedEvent]), }, reduce: (&userAuthMethodProjection{}).reduceRemoveAuthMethod, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -496,16 +487,16 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { name: "org reduceOwnerRemoved", reduce: (&userAuthMethodProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -522,17 +513,17 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(UserAuthMethodInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/user_grant.go b/internal/query/projection/user_grant.go index c02187771..43f8ae8b9 100644 --- a/internal/query/projection/user_grant.go +++ b/internal/query/projection/user_grant.go @@ -8,8 +8,8 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -41,53 +41,55 @@ const ( ) type userGrantProjection struct { - crdb.StatementHandler + es handler.EventStore } -func newUserGrantProjection(ctx context.Context, config crdb.StatementHandlerConfig) *userGrantProjection { - p := new(userGrantProjection) - config.ProjectionName = UserGrantProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(UserGrantID, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserGrantChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserGrantSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(UserGrantState, crdb.ColumnTypeEnum), - crdb.NewColumn(UserGrantResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantUserID, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantResourceOwnerUser, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantUserOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(UserGrantProjectID, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantResourceOwnerProject, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantProjectOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(UserGrantGrantID, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantGrantedOrg, crdb.ColumnTypeText), - crdb.NewColumn(UserGrantGrantedOrgRemoved, crdb.ColumnTypeBool, crdb.Default(false)), - crdb.NewColumn(UserGrantRoles, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(UserGrantOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func newUserGrantProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, &userGrantProjection{es: config.Eventstore}) +} + +func (*userGrantProjection) Name() string { + return UserGrantProjectionTable +} + +func (*userGrantProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(UserGrantID, handler.ColumnTypeText), + handler.NewColumn(UserGrantCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(UserGrantChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(UserGrantSequence, handler.ColumnTypeInt64), + handler.NewColumn(UserGrantState, handler.ColumnTypeEnum), + handler.NewColumn(UserGrantResourceOwner, handler.ColumnTypeText), + handler.NewColumn(UserGrantInstanceID, handler.ColumnTypeText), + handler.NewColumn(UserGrantUserID, handler.ColumnTypeText), + handler.NewColumn(UserGrantResourceOwnerUser, handler.ColumnTypeText), + handler.NewColumn(UserGrantUserOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(UserGrantProjectID, handler.ColumnTypeText), + handler.NewColumn(UserGrantResourceOwnerProject, handler.ColumnTypeText), + handler.NewColumn(UserGrantProjectOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(UserGrantGrantID, handler.ColumnTypeText), + handler.NewColumn(UserGrantGrantedOrg, handler.ColumnTypeText), + handler.NewColumn(UserGrantGrantedOrgRemoved, handler.ColumnTypeBool, handler.Default(false)), + handler.NewColumn(UserGrantRoles, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(UserGrantOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(UserGrantInstanceID, UserGrantID), - crdb.WithIndex(crdb.NewIndex("user_id", []string{UserGrantUserID})), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{UserGrantResourceOwner})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{UserGrantOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("user_owner_removed", []string{UserGrantUserOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("project_owner_removed", []string{UserGrantProjectOwnerRemoved})), - crdb.WithIndex(crdb.NewIndex("granted_org_removed", []string{UserGrantGrantedOrgRemoved})), + handler.NewPrimaryKey(UserGrantInstanceID, UserGrantID), + handler.WithIndex(handler.NewIndex("user_id", []string{UserGrantUserID})), + handler.WithIndex(handler.NewIndex("resource_owner", []string{UserGrantResourceOwner})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{UserGrantOwnerRemoved})), + handler.WithIndex(handler.NewIndex("user_owner_removed", []string{UserGrantUserOwnerRemoved})), + handler.WithIndex(handler.NewIndex("project_owner_removed", []string{UserGrantProjectOwnerRemoved})), + handler.WithIndex(handler.NewIndex("granted_org_removed", []string{UserGrantGrantedOrgRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *userGrantProjection) reducers() []handler.AggregateReducer { +func (p *userGrantProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: usergrant.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: usergrant.UserGrantAddedType, Reduce: p.reduceAdded, @@ -120,7 +122,7 @@ func (p *userGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.UserRemovedType, Reduce: p.reduceUserRemoved, @@ -129,7 +131,7 @@ func (p *userGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: project.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: project.ProjectRemovedType, Reduce: p.reduceProjectRemoved, @@ -154,7 +156,7 @@ func (p *userGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -163,7 +165,7 @@ func (p *userGrantProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(UserGrantInstanceID), @@ -180,7 +182,7 @@ func (p *userGrantProjection) reduceAdded(event eventstore.Event) (*handler.Stat } ctx := setUserGrantContext(e.Aggregate()) - userOwner, err := getResourceOwnerOfUser(ctx, p.Eventstore, e.Aggregate().InstanceID, e.UserID) + userOwner, err := getResourceOwnerOfUser(ctx, p.es, e.Aggregate().InstanceID, e.UserID) if err != nil { return nil, err } @@ -188,25 +190,25 @@ func (p *userGrantProjection) reduceAdded(event eventstore.Event) (*handler.Stat projectOwner := "" grantOwner := "" if e.ProjectGrantID != "" { - grantOwner, err = getGrantedOrgOfGrantedProject(ctx, p.Eventstore, e.Aggregate().InstanceID, e.ProjectID, e.ProjectGrantID) + grantOwner, err = getGrantedOrgOfGrantedProject(ctx, p.es, e.Aggregate().InstanceID, e.ProjectID, e.ProjectGrantID) if err != nil { return nil, err } } else { - projectOwner, err = getResourceOwnerOfProject(ctx, p.Eventstore, e.Aggregate().InstanceID, e.ProjectID) + projectOwner, err = getResourceOwnerOfProject(ctx, p.es, e.Aggregate().InstanceID, e.ProjectID) if err != nil { return nil, err } } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(UserGrantID, e.Aggregate().ID), handler.NewCol(UserGrantResourceOwner, e.Aggregate().ResourceOwner), handler.NewCol(UserGrantInstanceID, e.Aggregate().InstanceID), - handler.NewCol(UserGrantCreationDate, e.CreationDate()), - handler.NewCol(UserGrantChangeDate, e.CreationDate()), + handler.NewCol(UserGrantCreationDate, e.CreatedAt()), + handler.NewCol(UserGrantChangeDate, e.CreatedAt()), handler.NewCol(UserGrantSequence, e.Sequence()), handler.NewCol(UserGrantUserID, e.UserID), handler.NewCol(UserGrantResourceOwnerUser, userOwner), @@ -214,14 +216,14 @@ func (p *userGrantProjection) reduceAdded(event eventstore.Event) (*handler.Stat handler.NewCol(UserGrantResourceOwnerProject, projectOwner), handler.NewCol(UserGrantGrantID, e.ProjectGrantID), handler.NewCol(UserGrantGrantedOrg, grantOwner), - handler.NewCol(UserGrantRoles, database.StringArray(e.RoleKeys)), + handler.NewCol(UserGrantRoles, database.TextArray[string](e.RoleKeys)), handler.NewCol(UserGrantState, domain.UserGrantStateActive), }, ), nil } func (p *userGrantProjection) reduceChanged(event eventstore.Event) (*handler.Statement, error) { - var roles database.StringArray + var roles database.TextArray[string] switch e := event.(type) { case *usergrant.UserGrantChangedEvent: @@ -232,10 +234,10 @@ func (p *userGrantProjection) reduceChanged(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-hOr1E", "reduce.wrong.event.type %v", []eventstore.EventType{usergrant.UserGrantChangedType, usergrant.UserGrantCascadeChangedType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(UserGrantChangeDate, event.CreationDate()), + handler.NewCol(UserGrantChangeDate, event.CreatedAt()), handler.NewCol(UserGrantRoles, roles), handler.NewCol(UserGrantSequence, event.Sequence()), }, @@ -254,7 +256,7 @@ func (p *userGrantProjection) reduceRemoved(event eventstore.Event) (*handler.St return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-7OBEC", "reduce.wrong.event.type %v", []eventstore.EventType{usergrant.UserGrantRemovedType, usergrant.UserGrantCascadeRemovedType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(UserGrantID, event.Aggregate().ID), @@ -268,10 +270,10 @@ func (p *userGrantProjection) reduceDeactivated(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-oP7Gm", "reduce.wrong.event.type %s", usergrant.UserGrantDeactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(UserGrantChangeDate, event.CreationDate()), + handler.NewCol(UserGrantChangeDate, event.CreatedAt()), handler.NewCol(UserGrantState, domain.UserGrantStateInactive), handler.NewCol(UserGrantSequence, event.Sequence()), }, @@ -287,10 +289,10 @@ func (p *userGrantProjection) reduceReactivated(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-DGsKh", "reduce.wrong.event.type %s", usergrant.UserGrantReactivatedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - handler.NewCol(UserGrantChangeDate, event.CreationDate()), + handler.NewCol(UserGrantChangeDate, event.CreatedAt()), handler.NewCol(UserGrantState, domain.UserGrantStateActive), handler.NewCol(UserGrantSequence, event.Sequence()), }, @@ -306,7 +308,7 @@ func (p *userGrantProjection) reduceUserRemoved(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Bner2a", "reduce.wrong.event.type %s", user.UserRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(UserGrantUserID, event.Aggregate().ID), @@ -320,7 +322,7 @@ func (p *userGrantProjection) reduceProjectRemoved(event eventstore.Event) (*han return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Bne2a", "reduce.wrong.event.type %s", project.ProjectRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(UserGrantProjectID, event.Aggregate().ID), @@ -335,7 +337,7 @@ func (p *userGrantProjection) reduceProjectGrantRemoved(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-dGr2a", "reduce.wrong.event.type %s", project.GrantRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(UserGrantGrantID, e.GrantID), @@ -350,10 +352,10 @@ func (p *userGrantProjection) reduceRoleRemoved(event eventstore.Event) (*handle return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-dswg2", "reduce.wrong.event.type %s", project.RoleRemovedType) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - crdb.NewArrayRemoveCol(UserGrantRoles, e.Key), + handler.NewArrayRemoveCol(UserGrantRoles, e.Key), }, []handler.Condition{ handler.NewCond(UserGrantProjectID, e.Aggregate().ID), @@ -364,7 +366,7 @@ func (p *userGrantProjection) reduceRoleRemoved(event eventstore.Event) (*handle func (p *userGrantProjection) reduceProjectGrantChanged(event eventstore.Event) (*handler.Statement, error) { var grantID string - var keys []string + var keys database.TextArray[string] switch e := event.(type) { case *project.GrantChangedEvent: grantID = e.GrantID @@ -376,10 +378,10 @@ func (p *userGrantProjection) reduceProjectGrantChanged(event eventstore.Event) return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-Fh3gw", "reduce.wrong.event.type %v", []eventstore.EventType{project.GrantChangedType, project.GrantCascadeChangedType}) } - return crdb.NewUpdateStatement( + return handler.NewUpdateStatement( event, []handler.Column{ - crdb.NewArrayIntersectCol(UserGrantRoles, database.StringArray(keys)), + handler.NewArrayIntersectCol(UserGrantRoles, keys), }, []handler.Condition{ handler.NewCond(UserGrantGrantID, grantID), @@ -394,27 +396,27 @@ func (p *userGrantProjection) reduceOwnerRemoved(event eventstore.Event) (*handl return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-jpIvp", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewMultiStatement( + return handler.NewMultiStatement( e, - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(UserGrantInstanceID, e.Aggregate().InstanceID), handler.NewCond(UserGrantResourceOwner, e.Aggregate().ID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(UserGrantInstanceID, e.Aggregate().InstanceID), handler.NewCond(UserGrantResourceOwnerUser, e.Aggregate().ID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(UserGrantInstanceID, e.Aggregate().InstanceID), handler.NewCond(UserGrantResourceOwnerProject, e.Aggregate().ID), }, ), - crdb.AddDeleteStatement( + handler.AddDeleteStatement( []handler.Condition{ handler.NewCond(UserGrantInstanceID, e.Aggregate().InstanceID), handler.NewCond(UserGrantGrantedOrg, e.Aggregate().ID), @@ -423,10 +425,11 @@ func (p *userGrantProjection) reduceOwnerRemoved(event eventstore.Event) (*handl ), nil } -func getResourceOwnerOfUser(ctx context.Context, es *eventstore.Eventstore, instanceID, aggID string) (string, error) { +func getResourceOwnerOfUser(ctx context.Context, es handler.EventStore, instanceID, aggID string) (string, error) { events, err := es.Filter( ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). InstanceID(instanceID). AddQuery(). AggregateTypes(user.AggregateType). @@ -443,10 +446,11 @@ func getResourceOwnerOfUser(ctx context.Context, es *eventstore.Eventstore, inst return events[0].Aggregate().ResourceOwner, nil } -func getResourceOwnerOfProject(ctx context.Context, es *eventstore.Eventstore, instanceID, aggID string) (string, error) { +func getResourceOwnerOfProject(ctx context.Context, es handler.EventStore, instanceID, aggID string) (string, error) { events, err := es.Filter( ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). InstanceID(instanceID). AddQuery(). AggregateTypes(project.AggregateType). @@ -463,10 +467,11 @@ func getResourceOwnerOfProject(ctx context.Context, es *eventstore.Eventstore, i return events[0].Aggregate().ResourceOwner, nil } -func getGrantedOrgOfGrantedProject(ctx context.Context, es *eventstore.Eventstore, instanceID, projectID, grantID string) (string, error) { +func getGrantedOrgOfGrantedProject(ctx context.Context, es handler.EventStore, instanceID, projectID, grantID string) (string, error) { events, err := es.Filter( ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). InstanceID(instanceID). AddQuery(). AggregateTypes(project.AggregateType). @@ -490,6 +495,6 @@ func getGrantedOrgOfGrantedProject(ctx context.Context, es *eventstore.Eventstor return grantAddedEvent.GrantedOrgID, nil } -func setUserGrantContext(event eventstore.Aggregate) context.Context { - return authz.WithInstanceID(context.Background(), event.InstanceID) +func setUserGrantContext(aggregate *eventstore.Aggregate) context.Context { + return authz.WithInstanceID(context.Background(), aggregate.InstanceID) } diff --git a/internal/query/projection/user_grant_test.go b/internal/query/projection/user_grant_test.go index a2f98f4bb..97a464e37 100644 --- a/internal/query/projection/user_grant_test.go +++ b/internal/query/projection/user_grant_test.go @@ -10,9 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" @@ -33,30 +31,33 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantAddedType), - usergrant.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + usergrant.UserGrantAddedType, + usergrant.AggregateType, + []byte(`{ "userId": "user-id", "projectId": "project-id", "roleKeys": ["role"] }`), - ), usergrant.UserGrantAddedEventMapper), + ), usergrant.UserGrantAddedEventMapper), }, reduce: (&userGrantProjection{ - StatementHandler: getStatementHandlerWithFilters( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user-id", "org1").Aggregate, - "username1", - "firstname1", - "lastname1", - "nickname1", - "displayname1", - language.German, - domain.GenderMale, - "email1", - true, - ), + es: newMockEventStore(). + appendFilterResponse([]eventstore.Event{ + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user-id", "org1").Aggregate, + "username1", + "firstname1", + "lastname1", + "nickname1", + "displayname1", + language.German, + domain.GenderMale, + "email1", + true, + ), + }).appendFilterResponse([]eventstore.Event{ project.NewProjectAddedEvent(context.Background(), &project.NewAggregate("project-id", "org2").Aggregate, "project", @@ -65,11 +66,11 @@ func TestUserGrantProjection_reduces(t *testing.T) { false, domain.PrivateLabelingSettingUnspecified, ), - )(t)}).reduceAdded, + }), + }).reduceAdded, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -87,7 +88,7 @@ func TestUserGrantProjection_reduces(t *testing.T) { "org2", "", "", - database.StringArray{"role"}, + database.TextArray[string]{"role"}, domain.UserGrantStateActive, }, }, @@ -98,42 +99,45 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceAdded with projectgrant", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantAddedType), - usergrant.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + usergrant.UserGrantAddedType, + usergrant.AggregateType, + []byte(`{ "userId": "user-id", "projectId": "project-id", "grantId": "grant-id", "roleKeys": ["role"] }`), - ), usergrant.UserGrantAddedEventMapper), + ), usergrant.UserGrantAddedEventMapper), }, reduce: (&userGrantProjection{ - StatementHandler: getStatementHandlerWithFilters( - user.NewHumanAddedEvent(context.Background(), - &user.NewAggregate("user-id", "org1").Aggregate, - "username1", - "firstname1", - "lastname1", - "nickname1", - "displayname1", - language.German, - domain.GenderMale, - "email1", - true, - ), + es: newMockEventStore(). + appendFilterResponse([]eventstore.Event{ + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user-id", "org1").Aggregate, + "username1", + "firstname1", + "lastname1", + "nickname1", + "displayname1", + language.German, + domain.GenderMale, + "email1", + true, + ), + }).appendFilterResponse([]eventstore.Event{ project.NewGrantAddedEvent(context.Background(), &project.NewAggregate("project-id", "org2").Aggregate, "grant-id", "org3", []string{}, ), - )(t)}).reduceAdded, + }), + }).reduceAdded, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -151,7 +155,7 @@ func TestUserGrantProjection_reduces(t *testing.T) { "", "grant-id", "org3", - database.StringArray{"role"}, + database.TextArray[string]{"role"}, domain.UserGrantStateActive, }, }, @@ -162,26 +166,26 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantChangedType), - usergrant.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + usergrant.UserGrantChangedType, + usergrant.AggregateType, + []byte(`{ "roleKeys": ["role"] }`), - ), usergrant.UserGrantChangedEventMapper), + ), usergrant.UserGrantChangedEventMapper), }, reduce: (&userGrantProjection{}).reduceChanged, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.user_grants3 SET (change_date, roles, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, uint64(15), "agg-id", "instance-id", @@ -194,26 +198,26 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceCascadeChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantCascadeChangedType), - usergrant.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + usergrant.UserGrantCascadeChangedType, + usergrant.AggregateType, + []byte(`{ "roleKeys": ["role"] }`), - ), usergrant.UserGrantCascadeChangedEventMapper), + ), usergrant.UserGrantCascadeChangedEventMapper), }, reduce: (&userGrantProjection{}).reduceChanged, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.user_grants3 SET (change_date, roles, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, - database.StringArray{"role"}, + database.TextArray[string]{"role"}, uint64(15), "agg-id", "instance-id", @@ -226,17 +230,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantRemovedType), - usergrant.AggregateType, - nil, - ), usergrant.UserGrantRemovedEventMapper), + event: getEvent( + testEvent( + usergrant.UserGrantRemovedType, + usergrant.AggregateType, + nil, + ), usergrant.UserGrantRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -253,17 +257,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(UserGrantInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -279,17 +283,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceCascadeRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantCascadeRemovedType), - usergrant.AggregateType, - nil, - ), usergrant.UserGrantCascadeRemovedEventMapper), + event: getEvent( + testEvent( + usergrant.UserGrantCascadeRemovedType, + usergrant.AggregateType, + nil, + ), usergrant.UserGrantCascadeRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceRemoved, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -306,17 +310,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantDeactivatedType), - usergrant.AggregateType, - nil, - ), usergrant.UserGrantDeactivatedEventMapper), + event: getEvent( + testEvent( + usergrant.UserGrantDeactivatedType, + usergrant.AggregateType, + nil, + ), usergrant.UserGrantDeactivatedEventMapper), }, reduce: (&userGrantProjection{}).reduceDeactivated, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -336,17 +340,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(usergrant.UserGrantReactivatedType), - usergrant.AggregateType, - nil, - ), usergrant.UserGrantDeactivatedEventMapper), + event: getEvent( + testEvent( + usergrant.UserGrantReactivatedType, + usergrant.AggregateType, + nil, + ), usergrant.UserGrantDeactivatedEventMapper), }, reduce: (&userGrantProjection{}).reduceReactivated, want: wantReduce{ - aggregateType: usergrant.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: usergrant.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -366,17 +370,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceUserRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - nil, - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + nil, + ), user.UserRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -393,17 +397,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceProjectRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.ProjectRemovedType), - project.AggregateType, - nil, - ), project.ProjectRemovedEventMapper), + event: getEvent( + testEvent( + project.ProjectRemovedType, + project.AggregateType, + nil, + ), project.ProjectRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceProjectRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -420,17 +424,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantRemovedType), - project.AggregateType, - []byte(`{"grantId": "grantID"}`), - ), project.GrantRemovedEventMapper), + event: getEvent( + testEvent( + project.GrantRemovedType, + project.AggregateType, + []byte(`{"grantId": "grantID"}`), + ), project.GrantRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceProjectGrantRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -447,17 +451,17 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceRoleRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(project.RoleRemovedType), - project.AggregateType, - []byte(`{"key": "key"}`), - ), project.RoleRemovedEventMapper), + event: getEvent( + testEvent( + project.RoleRemovedType, + project.AggregateType, + []byte(`{"key": "key"}`), + ), project.RoleRemovedEventMapper), }, reduce: (&userGrantProjection{}).reduceRoleRemoved, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -475,23 +479,23 @@ func TestUserGrantProjection_reduces(t *testing.T) { { name: "reduceProjectGrantChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(project.GrantChangedType), - project.AggregateType, - []byte(`{"grantId": "grantID", "roleKeys": ["key"]}`), - ), project.GrantChangedEventMapper), + event: getEvent( + testEvent( + project.GrantChangedType, + project.AggregateType, + []byte(`{"grantId": "grantID", "roleKeys": ["key"]}`), + ), project.GrantChangedEventMapper), }, reduce: (&userGrantProjection{}).reduceProjectGrantChanged, want: wantReduce{ - aggregateType: project.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: project.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { expectedStmt: "UPDATE projections.user_grants3 SET (roles) = (SELECT ARRAY( SELECT UNNEST(roles) INTERSECT SELECT UNNEST ($1::TEXT[]))) WHERE (grant_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ - database.StringArray{"key"}, + database.TextArray[string]{"key"}, "grantID", "instance-id", }, @@ -504,16 +508,16 @@ func TestUserGrantProjection_reduces(t *testing.T) { name: "org.reduceOwnerRemoved", reduce: (&userGrantProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -563,29 +567,3 @@ func TestUserGrantProjection_reduces(t *testing.T) { }) } } - -func getStatementHandlerWithFilters(events ...eventstore.Command) func(t *testing.T) crdb.StatementHandler { - filters := make([]expect, 0) - for _, event := range events { - filters = append(filters, - expectFilter( - eventFromEventPusher( - event, - ), - ), - ) - } - - return func(t *testing.T) crdb.StatementHandler { - return crdb.StatementHandler{ - ProjectionHandler: &handler.ProjectionHandler{ - Handler: handler.Handler{ - Eventstore: eventstoreExpect( - t, - filters..., - ), - }, - }, - } - } -} diff --git a/internal/query/projection/user_metadata.go b/internal/query/projection/user_metadata.go index d1b06e52e..326860cdf 100644 --- a/internal/query/projection/user_metadata.go +++ b/internal/query/projection/user_metadata.go @@ -5,8 +5,8 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -26,41 +26,41 @@ const ( UserMetadataColumnOwnerRemoved = "owner_removed" ) -type userMetadataProjection struct { - crdb.StatementHandler +type userMetadataProjection struct{} + +func newUserMetadataProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(userMetadataProjection)) } -func newUserMetadataProjection(ctx context.Context, config crdb.StatementHandlerConfig) *userMetadataProjection { - p := new(userMetadataProjection) - config.ProjectionName = UserMetadataProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(UserMetadataColumnUserID, crdb.ColumnTypeText), - crdb.NewColumn(UserMetadataColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserMetadataColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(UserMetadataColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(UserMetadataColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(UserMetadataColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(UserMetadataColumnKey, crdb.ColumnTypeText), - crdb.NewColumn(UserMetadataColumnValue, crdb.ColumnTypeBytes, crdb.Nullable()), - crdb.NewColumn(UserMetadataColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*userMetadataProjection) Name() string { + return UserMetadataProjectionTable +} + +func (*userMetadataProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(UserMetadataColumnUserID, handler.ColumnTypeText), + handler.NewColumn(UserMetadataColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(UserMetadataColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(UserMetadataColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(UserMetadataColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(UserMetadataColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(UserMetadataColumnKey, handler.ColumnTypeText), + handler.NewColumn(UserMetadataColumnValue, handler.ColumnTypeBytes, handler.Nullable()), + handler.NewColumn(UserMetadataColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(UserMetadataColumnInstanceID, UserMetadataColumnUserID, UserMetadataColumnKey), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{UserGrantResourceOwner})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{UserMetadataColumnOwnerRemoved})), + handler.NewPrimaryKey(UserMetadataColumnInstanceID, UserMetadataColumnUserID, UserMetadataColumnKey), + handler.WithIndex(handler.NewIndex("resource_owner", []string{UserGrantResourceOwner})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{UserMetadataColumnOwnerRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *userMetadataProjection) reducers() []handler.AggregateReducer { +func (p *userMetadataProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.MetadataSetType, Reduce: p.reduceMetadataSet, @@ -81,7 +81,7 @@ func (p *userMetadataProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -90,7 +90,7 @@ func (p *userMetadataProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(UserMetadataColumnInstanceID), @@ -105,7 +105,7 @@ func (p *userMetadataProjection) reduceMetadataSet(event eventstore.Event) (*han if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Ghn52", "reduce.wrong.event.type %s", user.MetadataSetType) } - return crdb.NewUpsertStatement( + return handler.NewUpsertStatement( e, []handler.Column{ handler.NewCol(UserMetadataColumnInstanceID, nil), @@ -130,7 +130,7 @@ func (p *userMetadataProjection) reduceMetadataRemoved(event eventstore.Event) ( if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Bm542", "reduce.wrong.event.type %s", user.MetadataRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(UserMetadataColumnUserID, e.Aggregate().ID), @@ -148,7 +148,7 @@ func (p *userMetadataProjection) reduceMetadataRemovedAll(event eventstore.Event default: return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Bmnf2", "reduce.wrong.event.type %v", []eventstore.EventType{user.MetadataRemovedAllType, user.UserRemovedType}) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( event, []handler.Condition{ handler.NewCond(UserMetadataColumnUserID, event.Aggregate().ID), @@ -163,7 +163,7 @@ func (p *userMetadataProjection) reduceOwnerRemoved(event eventstore.Event) (*ha return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-oqwul", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(UserMetadataColumnInstanceID, e.Aggregate().InstanceID), diff --git a/internal/query/projection/user_metadata_test.go b/internal/query/projection/user_metadata_test.go index 923614c41..fcf8b26d2 100644 --- a/internal/query/projection/user_metadata_test.go +++ b/internal/query/projection/user_metadata_test.go @@ -5,8 +5,7 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -25,20 +24,20 @@ func TestUserMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataSet", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MetadataSetType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MetadataSetType, + user.AggregateType, + []byte(`{ "key": "key", "value": "dmFsdWU=" }`), - ), user.MetadataSetEventMapper), + ), user.MetadataSetEventMapper), }, reduce: (&userMetadataProjection{}).reduceMetadataSet, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -61,19 +60,19 @@ func TestUserMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MetadataRemovedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MetadataRemovedType, + user.AggregateType, + []byte(`{ "key": "key" }`), - ), user.MetadataRemovedEventMapper), + ), user.MetadataRemovedEventMapper), }, reduce: (&userMetadataProjection{}).reduceMetadataRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -91,17 +90,17 @@ func TestUserMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataRemovedAll", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MetadataRemovedAllType), - user.AggregateType, - nil, - ), user.MetadataRemovedAllEventMapper), + event: getEvent( + testEvent( + user.MetadataRemovedAllType, + user.AggregateType, + nil, + ), user.MetadataRemovedAllEventMapper), }, reduce: (&userMetadataProjection{}).reduceMetadataRemovedAll, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -118,17 +117,17 @@ func TestUserMetadataProjection_reduces(t *testing.T) { { name: "reduceMetadataRemovedAll (user removed)", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - nil, - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + nil, + ), user.UserRemovedEventMapper), }, reduce: (&userMetadataProjection{}).reduceMetadataRemovedAll, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -146,16 +145,16 @@ func TestUserMetadataProjection_reduces(t *testing.T) { name: "org reduceOwnerRemoved", reduce: (&userMetadataProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -172,17 +171,17 @@ func TestUserMetadataProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(UserMetadataColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/user_personal_access_token.go b/internal/query/projection/user_personal_access_token.go index 7ab5e494b..2c072d3bd 100644 --- a/internal/query/projection/user_personal_access_token.go +++ b/internal/query/projection/user_personal_access_token.go @@ -6,8 +6,8 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" + old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -28,43 +28,43 @@ const ( PersonalAccessTokenColumnOwnerRemoved = "owner_removed" ) -type personalAccessTokenProjection struct { - crdb.StatementHandler +type personalAccessTokenProjection struct{} + +func newPersonalAccessTokenProjection(ctx context.Context, config handler.Config) *handler.Handler { + return handler.NewHandler(ctx, &config, new(personalAccessTokenProjection)) } -func newPersonalAccessTokenProjection(ctx context.Context, config crdb.StatementHandlerConfig) *personalAccessTokenProjection { - p := new(personalAccessTokenProjection) - config.ProjectionName = PersonalAccessTokenProjectionTable - config.Reducers = p.reducers() - config.InitCheck = crdb.NewTableCheck( - crdb.NewTable([]*crdb.Column{ - crdb.NewColumn(PersonalAccessTokenColumnID, crdb.ColumnTypeText), - crdb.NewColumn(PersonalAccessTokenColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(PersonalAccessTokenColumnChangeDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(PersonalAccessTokenColumnSequence, crdb.ColumnTypeInt64), - crdb.NewColumn(PersonalAccessTokenColumnResourceOwner, crdb.ColumnTypeText), - crdb.NewColumn(PersonalAccessTokenColumnInstanceID, crdb.ColumnTypeText), - crdb.NewColumn(PersonalAccessTokenColumnUserID, crdb.ColumnTypeText), - crdb.NewColumn(PersonalAccessTokenColumnExpiration, crdb.ColumnTypeTimestamp), - crdb.NewColumn(PersonalAccessTokenColumnScopes, crdb.ColumnTypeTextArray, crdb.Nullable()), - crdb.NewColumn(PersonalAccessTokenColumnOwnerRemoved, crdb.ColumnTypeBool, crdb.Default(false)), +func (*personalAccessTokenProjection) Name() string { + return PersonalAccessTokenProjectionTable +} + +func (*personalAccessTokenProjection) Init() *old_handler.Check { + return handler.NewTableCheck( + handler.NewTable([]*handler.InitColumn{ + handler.NewColumn(PersonalAccessTokenColumnID, handler.ColumnTypeText), + handler.NewColumn(PersonalAccessTokenColumnCreationDate, handler.ColumnTypeTimestamp), + handler.NewColumn(PersonalAccessTokenColumnChangeDate, handler.ColumnTypeTimestamp), + handler.NewColumn(PersonalAccessTokenColumnSequence, handler.ColumnTypeInt64), + handler.NewColumn(PersonalAccessTokenColumnResourceOwner, handler.ColumnTypeText), + handler.NewColumn(PersonalAccessTokenColumnInstanceID, handler.ColumnTypeText), + handler.NewColumn(PersonalAccessTokenColumnUserID, handler.ColumnTypeText), + handler.NewColumn(PersonalAccessTokenColumnExpiration, handler.ColumnTypeTimestamp), + handler.NewColumn(PersonalAccessTokenColumnScopes, handler.ColumnTypeTextArray, handler.Nullable()), + handler.NewColumn(PersonalAccessTokenColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)), }, - crdb.NewPrimaryKey(PersonalAccessTokenColumnInstanceID, PersonalAccessTokenColumnID), - crdb.WithIndex(crdb.NewIndex("user_id", []string{PersonalAccessTokenColumnUserID})), - crdb.WithIndex(crdb.NewIndex("resource_owner", []string{PersonalAccessTokenColumnResourceOwner})), - crdb.WithIndex(crdb.NewIndex("owner_removed", []string{PersonalAccessTokenColumnOwnerRemoved})), + handler.NewPrimaryKey(PersonalAccessTokenColumnInstanceID, PersonalAccessTokenColumnID), + handler.WithIndex(handler.NewIndex("user_id", []string{PersonalAccessTokenColumnUserID})), + handler.WithIndex(handler.NewIndex("resource_owner", []string{PersonalAccessTokenColumnResourceOwner})), + handler.WithIndex(handler.NewIndex("owner_removed", []string{PersonalAccessTokenColumnOwnerRemoved})), ), ) - - p.StatementHandler = crdb.NewStatementHandler(ctx, config) - return p } -func (p *personalAccessTokenProjection) reducers() []handler.AggregateReducer { +func (p *personalAccessTokenProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: user.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: user.PersonalAccessTokenAddedType, Reduce: p.reducePersonalAccessTokenAdded, @@ -81,7 +81,7 @@ func (p *personalAccessTokenProjection) reducers() []handler.AggregateReducer { }, { Aggregate: org.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: org.OrgRemovedEventType, Reduce: p.reduceOwnerRemoved, @@ -90,7 +90,7 @@ func (p *personalAccessTokenProjection) reducers() []handler.AggregateReducer { }, { Aggregate: instance.AggregateType, - EventRedusers: []handler.EventReducer{ + EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(PersonalAccessTokenColumnInstanceID), @@ -105,7 +105,7 @@ func (p *personalAccessTokenProjection) reducePersonalAccessTokenAdded(event eve if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-DVgf7", "reduce.wrong.event.type %s", user.PersonalAccessTokenAddedType) } - return crdb.NewCreateStatement( + return handler.NewCreateStatement( e, []handler.Column{ handler.NewCol(PersonalAccessTokenColumnID, e.TokenID), @@ -116,7 +116,7 @@ func (p *personalAccessTokenProjection) reducePersonalAccessTokenAdded(event eve handler.NewCol(PersonalAccessTokenColumnSequence, e.Sequence()), handler.NewCol(PersonalAccessTokenColumnUserID, e.Aggregate().ID), handler.NewCol(PersonalAccessTokenColumnExpiration, e.Expiration), - handler.NewCol(PersonalAccessTokenColumnScopes, database.StringArray(e.Scopes)), + handler.NewCol(PersonalAccessTokenColumnScopes, database.TextArray[string](e.Scopes)), }, ), nil } @@ -126,7 +126,7 @@ func (p *personalAccessTokenProjection) reducePersonalAccessTokenRemoved(event e if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-g7u3F", "reduce.wrong.event.type %s", user.PersonalAccessTokenRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(PersonalAccessTokenColumnID, e.TokenID), @@ -140,7 +140,7 @@ func (p *personalAccessTokenProjection) reduceUserRemoved(event eventstore.Event if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dff3h", "reduce.wrong.event.type %s", user.UserRemovedType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(PersonalAccessTokenColumnUserID, e.Aggregate().ID), @@ -155,7 +155,7 @@ func (p *personalAccessTokenProjection) reduceOwnerRemoved(event eventstore.Even return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-zQVhl", "reduce.wrong.event.type %s", org.OrgRemovedEventType) } - return crdb.NewDeleteStatement( + return handler.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(PersonalAccessTokenColumnInstanceID, e.Aggregate().InstanceID), diff --git a/internal/query/projection/user_personal_access_token_test.go b/internal/query/projection/user_personal_access_token_test.go index a07229d71..4cb7e3162 100644 --- a/internal/query/projection/user_personal_access_token_test.go +++ b/internal/query/projection/user_personal_access_token_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -27,17 +26,17 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { { name: "reducePersonalAccessTokenAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.PersonalAccessTokenAddedType), - user.AggregateType, - []byte(`{"tokenId": "tokenID", "expiration": "9999-12-31T23:59:59Z", "scopes": ["openid"]}`), - ), user.PersonalAccessTokenAddedEventMapper), + event: getEvent( + testEvent( + user.PersonalAccessTokenAddedType, + user.AggregateType, + []byte(`{"tokenId": "tokenID", "expiration": "9999-12-31T23:59:59Z", "scopes": ["openid"]}`), + ), user.PersonalAccessTokenAddedEventMapper), }, reduce: (&personalAccessTokenProjection{}).reducePersonalAccessTokenAdded, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -51,7 +50,7 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { uint64(15), "agg-id", time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - database.StringArray{"openid"}, + database.TextArray[string]{"openid"}, }, }, }, @@ -61,17 +60,17 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { { name: "reducePersonalAccessTokenRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.PersonalAccessTokenRemovedType), - user.AggregateType, - []byte(`{"tokenId": "tokenID"}`), - ), user.PersonalAccessTokenRemovedEventMapper), + event: getEvent( + testEvent( + user.PersonalAccessTokenRemovedType, + user.AggregateType, + []byte(`{"tokenId": "tokenID"}`), + ), user.PersonalAccessTokenRemovedEventMapper), }, reduce: (&personalAccessTokenProjection{}).reducePersonalAccessTokenRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -88,17 +87,17 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { { name: "reduceUserRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.PersonalAccessTokenRemovedType), - user.AggregateType, - nil, - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.PersonalAccessTokenRemovedType, + user.AggregateType, + nil, + ), user.UserRemovedEventMapper), }, reduce: (&personalAccessTokenProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: eventstore.AggregateType("user"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("user"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -116,16 +115,16 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { name: "org reduceOwnerRemoved", reduce: (&personalAccessTokenProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -142,17 +141,17 @@ func TestPersonalAccessTokenProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(PersonalAccessTokenColumnInstanceID), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/projection/user_test.go b/internal/query/projection/user_test.go index 7651e75d5..66b4f6594 100644 --- a/internal/query/projection/user_test.go +++ b/internal/query/projection/user_test.go @@ -7,8 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/user" @@ -27,10 +26,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanAddedType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", @@ -41,13 +41,12 @@ func TestUserProjection_reduces(t *testing.T) { "email": "email@zitadel.com", "phone": "+41 00 000 00 00" }`), - ), user.HumanAddedEventMapper), + ), user.HumanAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -96,10 +95,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1Added", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1AddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserV1AddedType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", @@ -110,13 +110,12 @@ func TestUserProjection_reduces(t *testing.T) { "email": "email@zitadel.com", "phone": "+41 00 000 00 00" }`), - ), user.HumanAddedEventMapper), + ), user.HumanAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -165,22 +164,22 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanAdded NULLs", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanAddedType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", "email": "email@zitadel.com" }`), - ), user.HumanAddedEventMapper), + ), user.HumanAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -229,10 +228,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanRegistered", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanRegisteredType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanRegisteredType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", @@ -243,13 +243,12 @@ func TestUserProjection_reduces(t *testing.T) { "email": "email@zitadel.com", "phone": "+41 00 000 00 00" }`), - ), user.HumanRegisteredEventMapper), + ), user.HumanRegisteredEventMapper), }, reduce: (&userProjection{}).reduceHumanRegistered, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -298,10 +297,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1Registered", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1RegisteredType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserV1RegisteredType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", @@ -312,13 +312,12 @@ func TestUserProjection_reduces(t *testing.T) { "email": "email@zitadel.com", "phone": "+41 00 000 00 00" }`), - ), user.HumanRegisteredEventMapper), + ), user.HumanRegisteredEventMapper), }, reduce: (&userProjection{}).reduceHumanRegistered, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -367,22 +366,22 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanRegistered NULLs", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanRegisteredType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanRegisteredType, + user.AggregateType, + []byte(`{ "username": "user-name", "firstName": "first-name", "lastName": "last-name", "email": "email@zitadel.com" }`), - ), user.HumanRegisteredEventMapper), + ), user.HumanRegisteredEventMapper), }, reduce: (&userProjection{}).reduceHumanRegistered, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -431,17 +430,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanInitCodeAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanInitialCodeAddedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanInitialCodeAddedEventMapper), + event: getEvent( + testEvent( + user.HumanInitialCodeAddedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanInitialCodeAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanInitCodeAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -459,17 +458,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1InitCodeAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1InitialCodeAddedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanInitialCodeAddedEventMapper), + event: getEvent( + testEvent( + user.UserV1InitialCodeAddedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanInitialCodeAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanInitCodeAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -487,17 +486,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanInitCodeSucceeded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanInitializedCheckSucceededType), - user.AggregateType, - []byte(`{}`), - ), user.HumanInitializedCheckSucceededEventMapper), + event: getEvent( + testEvent( + user.HumanInitializedCheckSucceededType, + user.AggregateType, + []byte(`{}`), + ), user.HumanInitializedCheckSucceededEventMapper), }, reduce: (&userProjection{}).reduceHumanInitCodeSucceeded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -515,17 +514,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1InitCodeAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1InitializedCheckSucceededType), - user.AggregateType, - []byte(`{}`), - ), user.HumanInitializedCheckSucceededEventMapper), + event: getEvent( + testEvent( + user.UserV1InitializedCheckSucceededType, + user.AggregateType, + []byte(`{}`), + ), user.HumanInitializedCheckSucceededEventMapper), }, reduce: (&userProjection{}).reduceHumanInitCodeSucceeded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -543,17 +542,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserLocked", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserLockedType), - user.AggregateType, - []byte(`{}`), - ), user.UserLockedEventMapper), + event: getEvent( + testEvent( + user.UserLockedType, + user.AggregateType, + []byte(`{}`), + ), user.UserLockedEventMapper), }, reduce: (&userProjection{}).reduceUserLocked, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -573,17 +572,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserUnlocked", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserUnlockedType), - user.AggregateType, - []byte(`{}`), - ), user.UserUnlockedEventMapper), + event: getEvent( + testEvent( + user.UserUnlockedType, + user.AggregateType, + []byte(`{}`), + ), user.UserUnlockedEventMapper), }, reduce: (&userProjection{}).reduceUserUnlocked, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -603,17 +602,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserDeactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserDeactivatedType), - user.AggregateType, - []byte(`{}`), - ), user.UserDeactivatedEventMapper), + event: getEvent( + testEvent( + user.UserDeactivatedType, + user.AggregateType, + []byte(`{}`), + ), user.UserDeactivatedEventMapper), }, reduce: (&userProjection{}).reduceUserDeactivated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -633,17 +632,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserReactivated", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserReactivatedType), - user.AggregateType, - []byte(`{}`), - ), user.UserReactivatedEventMapper), + event: getEvent( + testEvent( + user.UserReactivatedType, + user.AggregateType, + []byte(`{}`), + ), user.UserReactivatedEventMapper), }, reduce: (&userProjection{}).reduceUserReactivated, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -663,17 +662,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.UserRemovedEventMapper), + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.UserRemovedEventMapper), }, reduce: (&userProjection{}).reduceUserRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -690,19 +689,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserUserNameChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserUserNameChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserUserNameChangedType, + user.AggregateType, + []byte(`{ "username": "username" }`), - ), user.UsernameChangedEventMapper), + ), user.UsernameChangedEventMapper), }, reduce: (&userProjection{}).reduceUserNameChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -722,19 +721,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceDomainClaimed", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserDomainClaimedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserDomainClaimedType, + user.AggregateType, + []byte(`{ "username": "id@temporary.domain" }`), - ), user.DomainClaimedEventMapper), + ), user.DomainClaimedEventMapper), }, reduce: (&userProjection{}).reduceDomainClaimed, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -754,10 +753,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanProfileChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanProfileChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanProfileChangedType, + user.AggregateType, + []byte(`{ "firstName": "first-name", "lastName": "last-name", "nickName": "nick-name", @@ -765,13 +765,12 @@ func TestUserProjection_reduces(t *testing.T) { "preferredLanguage": "ch-DE", "gender": 3 }`), - ), user.HumanProfileChangedEventMapper), + ), user.HumanProfileChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanProfileChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -803,10 +802,11 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1ProfileChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1ProfileChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserV1ProfileChangedType, + user.AggregateType, + []byte(`{ "firstName": "first-name", "lastName": "last-name", "nickName": "nick-name", @@ -814,13 +814,12 @@ func TestUserProjection_reduces(t *testing.T) { "preferredLanguage": "ch-DE", "gender": 3 }`), - ), user.HumanProfileChangedEventMapper), + ), user.HumanProfileChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanProfileChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -852,19 +851,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanPhoneChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanPhoneChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanPhoneChangedType, + user.AggregateType, + []byte(`{ "phone": "+41 00 000 00 00" }`), - ), user.HumanPhoneChangedEventMapper), + ), user.HumanPhoneChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -900,19 +899,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1PhoneChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1PhoneChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserV1PhoneChangedType, + user.AggregateType, + []byte(`{ "phone": "+41 00 000 00 00" }`), - ), user.HumanPhoneChangedEventMapper), + ), user.HumanPhoneChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -948,17 +947,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanPhoneRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanPhoneRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanPhoneRemovedEventMapper), + event: getEvent( + testEvent( + user.HumanPhoneRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanPhoneRemovedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -995,17 +994,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1PhoneRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1PhoneRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanPhoneRemovedEventMapper), + event: getEvent( + testEvent( + user.UserV1PhoneRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanPhoneRemovedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1042,17 +1041,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanPhoneVerified", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanPhoneVerifiedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanPhoneVerifiedEventMapper), + event: getEvent( + testEvent( + user.HumanPhoneVerifiedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanPhoneVerifiedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneVerified, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1086,17 +1085,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1PhoneVerified", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1PhoneVerifiedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanPhoneVerifiedEventMapper), + event: getEvent( + testEvent( + user.UserV1PhoneVerifiedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanPhoneVerifiedEventMapper), }, reduce: (&userProjection{}).reduceHumanPhoneVerified, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1130,19 +1129,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanEmailChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanEmailChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanEmailChangedType, + user.AggregateType, + []byte(`{ "email": "email@zitadel.com" }`), - ), user.HumanEmailChangedEventMapper), + ), user.HumanEmailChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanEmailChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1178,19 +1177,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1EmailChanged", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1EmailChangedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.UserV1EmailChangedType, + user.AggregateType, + []byte(`{ "email": "email@zitadel.com" }`), - ), user.HumanEmailChangedEventMapper), + ), user.HumanEmailChangedEventMapper), }, reduce: (&userProjection{}).reduceHumanEmailChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1226,17 +1225,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanEmailVerified", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanEmailVerifiedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanEmailVerifiedEventMapper), + event: getEvent( + testEvent( + user.HumanEmailVerifiedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanEmailVerifiedEventMapper), }, reduce: (&userProjection{}).reduceHumanEmailVerified, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1270,17 +1269,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceUserV1EmailVerified", args: args{ - event: getEvent(testEvent( - repository.EventType(user.UserV1EmailVerifiedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanEmailVerifiedEventMapper), + event: getEvent( + testEvent( + user.UserV1EmailVerifiedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanEmailVerifiedEventMapper), }, reduce: (&userProjection{}).reduceHumanEmailVerified, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1314,19 +1313,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanAvatarAdded", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanAvatarAddedType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.HumanAvatarAddedType, + user.AggregateType, + []byte(`{ "storeKey": "users/agg-id/avatar" }`), - ), user.HumanAvatarAddedEventMapper), + ), user.HumanAvatarAddedEventMapper), }, reduce: (&userProjection{}).reduceHumanAvatarAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1353,17 +1352,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceHumanAvatarRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(user.HumanAvatarRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.HumanAvatarRemovedEventMapper), + event: getEvent( + testEvent( + user.HumanAvatarRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanAvatarRemovedEventMapper), }, reduce: (&userProjection{}).reduceHumanAvatarRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1390,20 +1389,20 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineAddedEvent no description", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineAddedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineAddedEventType, + user.AggregateType, + []byte(`{ "username": "username", "name": "machine-name" }`), - ), user.MachineAddedEventMapper), + ), user.MachineAddedEventMapper), }, reduce: (&userProjection{}).reduceMachineAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1437,21 +1436,21 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineAddedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineAddedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineAddedEventType, + user.AggregateType, + []byte(`{ "username": "username", "name": "machine-name", "description": "description" }`), - ), user.MachineAddedEventMapper), + ), user.MachineAddedEventMapper), }, reduce: (&userProjection{}).reduceMachineAdded, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1485,20 +1484,20 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineChangedEvent", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineChangedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineChangedEventType, + user.AggregateType, + []byte(`{ "name": "machine-name", "description": "description" }`), - ), user.MachineChangedEventMapper), + ), user.MachineChangedEventMapper), }, reduce: (&userProjection{}).reduceMachineChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1526,19 +1525,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineChangedEvent name", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineChangedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineChangedEventType, + user.AggregateType, + []byte(`{ "name": "machine-name" }`), - ), user.MachineChangedEventMapper), + ), user.MachineChangedEventMapper), }, reduce: (&userProjection{}).reduceMachineChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1565,19 +1564,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineChangedEvent description", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineChangedEventType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineChangedEventType, + user.AggregateType, + []byte(`{ "description": "description" }`), - ), user.MachineChangedEventMapper), + ), user.MachineChangedEventMapper), }, reduce: (&userProjection{}).reduceMachineChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1604,17 +1603,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineChangedEvent no values", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineChangedEventType), - user.AggregateType, - []byte(`{}`), - ), user.MachineChangedEventMapper), + event: getEvent( + testEvent( + user.MachineChangedEventType, + user.AggregateType, + []byte(`{}`), + ), user.MachineChangedEventMapper), }, reduce: (&userProjection{}).reduceMachineChanged, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{}, }, @@ -1623,19 +1622,19 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineSecretSet", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineSecretSetType), - user.AggregateType, - []byte(`{ + event: getEvent( + testEvent( + user.MachineSecretSetType, + user.AggregateType, + []byte(`{ "client_secret": {} }`), - ), user.MachineSecretSetEventMapper), + ), user.MachineSecretSetEventMapper), }, reduce: (&userProjection{}).reduceMachineSecretSet, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1662,17 +1661,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "reduceMachineSecretSet", args: args{ - event: getEvent(testEvent( - repository.EventType(user.MachineSecretRemovedType), - user.AggregateType, - []byte(`{}`), - ), user.MachineSecretRemovedEventMapper), + event: getEvent( + testEvent( + user.MachineSecretRemovedType, + user.AggregateType, + []byte(`{}`), + ), user.MachineSecretRemovedEventMapper), }, reduce: (&userProjection{}).reduceMachineSecretRemoved, want: wantReduce{ - aggregateType: user.AggregateType, - sequence: 15, - previousSequence: 10, + aggregateType: user.AggregateType, + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1700,16 +1699,16 @@ func TestUserProjection_reduces(t *testing.T) { name: "org reduceOwnerRemoved", reduce: (&userProjection{}).reduceOwnerRemoved, args: args{ - event: getEvent(testEvent( - repository.EventType(org.OrgRemovedEventType), - org.AggregateType, - nil, - ), org.OrgRemovedEventMapper), + event: getEvent( + testEvent( + org.OrgRemovedEventType, + org.AggregateType, + nil, + ), org.OrgRemovedEventMapper), }, want: wantReduce{ - aggregateType: eventstore.AggregateType("org"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("org"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { @@ -1726,17 +1725,17 @@ func TestUserProjection_reduces(t *testing.T) { { name: "instance reduceInstanceRemoved", args: args{ - event: getEvent(testEvent( - repository.EventType(instance.InstanceRemovedEventType), - instance.AggregateType, - nil, - ), instance.InstanceRemovedEventMapper), + event: getEvent( + testEvent( + instance.InstanceRemovedEventType, + instance.AggregateType, + nil, + ), instance.InstanceRemovedEventMapper), }, reduce: reduceInstanceRemovedHelper(UserInstanceIDCol), want: wantReduce{ - aggregateType: eventstore.AggregateType("instance"), - sequence: 15, - previousSequence: 10, + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, executer: &testExecuter{ executions: []execution{ { diff --git a/internal/query/query.go b/internal/query/query.go index 216426c43..2265183e1 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -12,6 +12,7 @@ import ( "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" + internal_authz "github.com/zitadel/zitadel/internal/api/authz" sd "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" @@ -61,6 +62,7 @@ func StartQueries( zitadelRoles []authz.RoleMapping, sessionTokenVerifier func(ctx context.Context, sessionToken string, sessionID string, tokenID string) (err error), permissionCheck func(q *Queries) domain.PermissionCheck, + systemAPIUsers map[string]*internal_authz.SystemAPIUser, ) (repo *Queries, err error) { statikLoginFS, err := fs.NewWithNamespace("login") if err != nil { @@ -106,11 +108,11 @@ func StartQueries( repo.checkPermission = permissionCheck(repo) - err = projection.Create(ctx, sqlClient, es, projections, keyEncryptionAlgorithm, certEncryptionAlgorithm) + err = projection.Create(ctx, sqlClient, es, projections, keyEncryptionAlgorithm, certEncryptionAlgorithm, systemAPIUsers) if err != nil { return nil, err } - projection.Start() + projection.Start(ctx) return repo, nil } diff --git a/internal/query/search_query.go b/internal/query/search_query.go index 63dd6739f..ce9d31238 100644 --- a/internal/query/search_query.go +++ b/internal/query/search_query.go @@ -11,7 +11,7 @@ import ( type SearchResponse struct { Count uint64 - *LatestSequence + *State } type SearchRequest struct { @@ -43,6 +43,7 @@ func (req *SearchRequest) toQuery(query sq.SelectBuilder) sq.SelectBuilder { type SearchQuery interface { toQuery(sq.SelectBuilder) sq.SelectBuilder comp() sq.Sqlizer + Col() Column } type NotNullQuery struct { @@ -66,6 +67,10 @@ func (q *NotNullQuery) comp() sq.Sqlizer { return sq.NotEq{q.Column.identifier(): nil} } +func (q *NotNullQuery) Col() Column { + return q.Column +} + type IsNullQuery struct { Column Column } @@ -87,6 +92,10 @@ func (q *IsNullQuery) comp() sq.Sqlizer { return sq.Eq{q.Column.identifier(): nil} } +func (q *IsNullQuery) Col() Column { + return q.Column +} + type orQuery struct { queries []SearchQuery } @@ -110,6 +119,10 @@ func (q *orQuery) comp() sq.Sqlizer { return or } +func (q *orQuery) Col() Column { + return Column{} +} + type ColumnComparisonQuery struct { Column1 Column Compare ColumnComparison @@ -137,6 +150,10 @@ func (q *ColumnComparisonQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder return query.Where(q.comp()) } +func (q *ColumnComparisonQuery) Col() Column { + return Column{} +} + func (s *ColumnComparisonQuery) comp() sq.Sqlizer { switch s.Compare { case ColumnEquals: @@ -160,19 +177,10 @@ type InTextQuery struct { Column Column Values []string } -type TextQuery struct { - Column Column - Text string - Compare TextComparison -} -var ( - ErrNothingSelected = errors.New("nothing selected") - ErrInvalidCompare = errors.New("invalid compare") - ErrMissingColumn = errors.New("missing column") - ErrInvalidNumber = errors.New("value is no number") - ErrEmptyValues = errors.New("values array must not be empty") -) +func (q *InTextQuery) Col() Column { + return q.Column +} func NewInTextQuery(col Column, values []string) (*InTextQuery, error) { if len(values) == 0 { @@ -187,6 +195,20 @@ func NewInTextQuery(col Column, values []string) (*InTextQuery, error) { }, nil } +type TextQuery struct { + Column Column + Text string + Compare TextComparison +} + +var ( + ErrNothingSelected = errors.New("nothing selected") + ErrInvalidCompare = errors.New("invalid compare") + ErrMissingColumn = errors.New("missing column") + ErrInvalidNumber = errors.New("value is no number") + ErrEmptyValues = errors.New("values array must not be empty") +) + func NewTextQuery(col Column, value string, compare TextComparison) (*TextQuery, error) { if compare < 0 || compare >= textCompareMax { return nil, ErrInvalidCompare @@ -201,6 +223,10 @@ func NewTextQuery(col Column, value string, compare TextComparison) (*TextQuery, }, nil } +func (q *TextQuery) Col() Column { + return q.Column +} + func (q *InTextQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return query.Where(q.comp()) } @@ -311,6 +337,10 @@ func (q *NumberQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return query.Where(q.comp()) } +func (q *NumberQuery) Col() Column { + return q.Column +} + func (s *NumberQuery) comp() sq.Sqlizer { switch s.Compare { case NumberEquals: @@ -427,6 +457,10 @@ func (s *ListQuery) comp() sq.Sqlizer { return nil } +func (q *ListQuery) Col() Column { + return q.Column +} + type ListComparison int const ( @@ -466,6 +500,10 @@ func (q *or) comp() sq.Sqlizer { return sq.Or(queries) } +func (q *or) Col() Column { + return Column{} +} + type BoolQuery struct { Column Column Value bool @@ -478,6 +516,10 @@ func NewBoolQuery(c Column, value bool) (*BoolQuery, error) { }, nil } +func (q *BoolQuery) Col() Column { + return q.Column +} + func (q *BoolQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return query.Where(q.comp()) } diff --git a/internal/query/secret_generators.go b/internal/query/secret_generators.go index 35c9ccbd8..f339668a2 100644 --- a/internal/query/secret_generators.go +++ b/internal/query/secret_generators.go @@ -174,7 +174,7 @@ func (q *Queries) SearchSecretGenerators(ctx context.Context, queries *SecretGen if err != nil { return nil, errors.ThrowInternal(err, "QUERY-4miii", "Errors.Internal") } - secretGenerators.LatestSequence, err = q.latestSequence(ctx, secretGeneratorsTable) + secretGenerators.State, err = q.latestState(ctx, secretGeneratorsTable) return secretGenerators, err } diff --git a/internal/query/security_policy.go b/internal/query/security_policy.go index 5a1b23dfb..49f355a45 100644 --- a/internal/query/security_policy.go +++ b/internal/query/security_policy.go @@ -54,7 +54,7 @@ type SecurityPolicy struct { Sequence uint64 Enabled bool - AllowedOrigins database.StringArray + AllowedOrigins database.TextArray[string] } func (q *Queries) SecurityPolicy(ctx context.Context) (policy *SecurityPolicy, err error) { diff --git a/internal/query/session.go b/internal/query/session.go index c098d3d11..bcc87a303 100644 --- a/internal/query/session.go +++ b/internal/query/session.go @@ -8,11 +8,14 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -170,7 +173,8 @@ func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, s defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.SessionProjection.Trigger(ctx) + ctx, err = projection.SessionProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("unable to trigger") } query, scan := prepareSessionQuery(ctx, q.client) @@ -222,7 +226,7 @@ func (q *Queries) SearchSessions(ctx context.Context, queries *SessionsSearchQue return nil, errors.ThrowInternal(err, "QUERY-Sfg42", "Errors.Internal") } - sessions.LatestSequence, err = q.latestSequence(ctx, sessionsTable) + sessions.State, err = q.latestState(ctx, sessionsTable) return sessions, err } diff --git a/internal/query/sms.go b/internal/query/sms.go index 956328b64..898f34a4d 100644 --- a/internal/query/sms.go +++ b/internal/query/sms.go @@ -181,7 +181,7 @@ func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearc if err != nil { return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal") } - configs.LatestSequence, err = q.latestSequence(ctx, smsConfigsTable) + configs.State, err = q.latestState(ctx, smsConfigsTable) return configs, err } diff --git a/internal/query/user.go b/internal/query/user.go index 9b421bdc9..ea71a7d47 100644 --- a/internal/query/user.go +++ b/internal/query/user.go @@ -5,9 +5,11 @@ import ( "database/sql" errs "errors" "strings" + "sync" "time" sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" @@ -15,6 +17,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -33,7 +36,7 @@ type User struct { State domain.UserState Type domain.UserType Username string - LoginNames database.StringArray + LoginNames database.TextArray[string] PreferredLoginName string Human *Human Machine *Machine @@ -104,7 +107,7 @@ type NotifyUser struct { State domain.UserState Type domain.UserType Username string - LoginNames database.StringArray + LoginNames database.TextArray[string] PreferredLoginName string FirstName string LastName string @@ -338,8 +341,7 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userI defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserProjection.Trigger(ctx) - ctx = projection.LoginNameProjection.Trigger(ctx) + triggerUserProjections(ctx) } query, scan := prepareUserQuery(ctx, q.client) @@ -370,8 +372,7 @@ func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwner defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserProjection.Trigger(ctx) - ctx = projection.LoginNameProjection.Trigger(ctx) + triggerUserProjections(ctx) } query, scan := prepareUserQuery(ctx, q.client) @@ -482,8 +483,7 @@ func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, u defer func() { span.EndWithError(err) }() if shouldTriggered { - ctx = projection.UserProjection.Trigger(ctx) - ctx = projection.LoginNameProjection.Trigger(ctx) + triggerUserProjections(ctx) } query, scan := prepareNotifyUserQuery(ctx, q.client) @@ -514,8 +514,7 @@ func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withO defer func() { span.EndWithError(err) }() if shouldTriggered { - ctx = projection.UserProjection.Trigger(ctx) - ctx = projection.LoginNameProjection.Trigger(ctx) + triggerUserProjections(ctx) } query, scan := prepareNotifyUserQuery(ctx, q.client) @@ -563,7 +562,7 @@ func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, w return nil, errors.ThrowInternal(err, "QUERY-AG4gs", "Errors.Internal") } - users.LatestSequence, err = q.latestSequence(ctx, userTable) + users.State, err = q.latestState(ctx, userTable) return users, err } @@ -718,6 +717,22 @@ func NewUserLoginNameExistsQuery(value string, comparison TextComparison) (Searc ) } +func triggerUserProjections(ctx context.Context) { + wg := sync.WaitGroup{} + wg.Add(2) + func() { + _, err := projection.UserProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") + wg.Done() + }() + func() { + _, err := projection.LoginNameProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") + wg.Done() + }() + wg.Wait() +} + func prepareLoginNamesQuery() (string, []interface{}, error) { return sq.Select( userLoginNamesUserIDCol.identifier(), @@ -1104,7 +1119,7 @@ func prepareNotifyUserQuery(ctx context.Context, db prepareDatabase) (sq.SelectB func(row *sql.Row) (*NotifyUser, error) { u := new(NotifyUser) var count int - loginNames := database.StringArray{} + loginNames := database.TextArray[string]{} preferredLoginName := sql.NullString{} humanID := sql.NullString{} @@ -1277,7 +1292,7 @@ func prepareUsersQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilde var count uint64 for rows.Next() { u := new(User) - loginNames := database.StringArray{} + loginNames := database.TextArray[string]{} preferredLoginName := sql.NullString{} humanID := sql.NullString{} diff --git a/internal/query/user_auth_method.go b/internal/query/user_auth_method.go index 0881c253c..956817977 100644 --- a/internal/query/user_auth_method.go +++ b/internal/query/user_auth_method.go @@ -137,7 +137,7 @@ func (q *Queries) SearchUserAuthMethods(ctx context.Context, queries *UserAuthMe if err != nil { return nil, err } - userAuthMethods.LatestSequence, err = q.latestSequence(ctx, userAuthMethodTable) + userAuthMethods.State, err = q.latestState(ctx, userAuthMethodTable) return userAuthMethods, err } @@ -171,7 +171,7 @@ func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID stri if err != nil { return nil, err } - userAuthMethodTypes.LatestSequence, err = q.latestSequence(ctx, userTable, notifyTable, userAuthMethodTable, idpUserLinkTable) + userAuthMethodTypes.State, err = q.latestState(ctx, userTable, notifyTable, userAuthMethodTable, idpUserLinkTable) return userAuthMethodTypes, err } diff --git a/internal/query/user_grant.go b/internal/query/user_grant.go index 9643b3d93..da36b0ef8 100644 --- a/internal/query/user_grant.go +++ b/internal/query/user_grant.go @@ -8,11 +8,14 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -23,7 +26,7 @@ type UserGrant struct { CreationDate time.Time ChangeDate time.Time Sequence uint64 - Roles database.StringArray + Roles database.TextArray[string] // GrantID represents the project grant id GrantID string State domain.UserGrantState @@ -237,7 +240,8 @@ func (q *Queries) UserGrant(ctx context.Context, shouldTriggerBulk bool, withOwn defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserGrantProjection.Trigger(ctx) + ctx, err = projection.UserGrantProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := prepareUserGrantQuery(ctx, q.client) @@ -265,7 +269,8 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserGrantProjection.Trigger(ctx) + ctx, err = projection.UserGrantProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("unable to trigger") } query, scan := prepareUserGrantsQuery(ctx, q.client) @@ -278,7 +283,7 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh return nil, errors.ThrowInternal(err, "QUERY-wXnQR", "Errors.Query.SQLStatement") } - latestSequence, err := q.latestSequence(ctx, userGrantTable) + latestSequence, err := q.latestState(ctx, userGrantTable) if err != nil { return nil, err } @@ -291,7 +296,7 @@ func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries, sh return nil, err } - grants.LatestSequence = latestSequence + grants.State = latestSequence return grants, nil } diff --git a/internal/query/user_grant_test.go b/internal/query/user_grant_test.go index bcbd70b4c..918003f61 100644 --- a/internal/query/user_grant_test.go +++ b/internal/query/user_grant_test.go @@ -149,7 +149,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -174,7 +174,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -207,7 +207,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -232,7 +232,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -265,7 +265,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -290,7 +290,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -323,7 +323,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -348,7 +348,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -381,7 +381,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -406,7 +406,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -469,7 +469,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -500,7 +500,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -536,7 +536,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -567,7 +567,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -603,7 +603,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -634,7 +634,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -670,7 +670,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -701,7 +701,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -737,7 +737,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -768,7 +768,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -804,7 +804,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -828,7 +828,7 @@ func Test_UserGrantPrepares(t *testing.T) { testNow, 20211111, "grant-id", - database.StringArray{"role-key"}, + database.TextArray[string]{"role-key"}, domain.UserGrantStateActive, "user-id", "username", @@ -859,7 +859,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", @@ -883,7 +883,7 @@ func Test_UserGrantPrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211111, - Roles: database.StringArray{"role-key"}, + Roles: database.TextArray[string]{"role-key"}, GrantID: "grant-id", State: domain.UserGrantStateActive, UserID: "user-id", diff --git a/internal/query/user_membership.go b/internal/query/user_membership.go index f2eae22a0..96d113983 100644 --- a/internal/query/user_membership.go +++ b/internal/query/user_membership.go @@ -3,6 +3,7 @@ package query import ( "context" "database/sql" + "sync" "time" sq "github.com/Masterminds/squirrel" @@ -11,6 +12,7 @@ import ( "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -22,7 +24,7 @@ type Memberships struct { type Membership struct { UserID string - Roles database.StringArray + Roles database.TextArray[string] CreationDate time.Time ChangeDate time.Time Sequence uint64 @@ -105,17 +107,48 @@ func (q *MembershipSearchQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder return query } -func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuery, withOwnerRemoved bool) (memberships *Memberships, err error) { +func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuery, withOwnerRemoved, shouldTrigger bool) (memberships *Memberships, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - query, queryArgs, scan := prepareMembershipsQuery(ctx, q.client, withOwnerRemoved) + if shouldTrigger { + wg := sync.WaitGroup{} + wg.Add(4) + go func() { + spanCtx, triggerSpan := tracing.NewNamedSpan(ctx, "TriggerOrgMemberProjection") + _, _ = projection.OrgMemberProjection.Trigger(spanCtx, handler.WithAwaitRunning()) + triggerSpan.End() + wg.Done() + }() + go func() { + spanCtx, triggerSpan := tracing.NewNamedSpan(ctx, "TriggerInstanceMemberProjection") + _, _ = projection.InstanceMemberProjection.Trigger(spanCtx, handler.WithAwaitRunning()) + triggerSpan.End() + wg.Done() + }() + go func() { + spanCtx, triggerSpan := tracing.NewNamedSpan(ctx, "TriggerProjectMemberProjection") + _, _ = projection.ProjectMemberProjection.Trigger(spanCtx, handler.WithAwaitRunning()) + triggerSpan.End() + wg.Done() + }() + go func() { + spanCtx, triggerSpan := tracing.NewNamedSpan(ctx, "TriggerProjectGrantMemberProjection") + _, _ = projection.ProjectGrantMemberProjection.Trigger(spanCtx, handler.WithAwaitRunning()) + triggerSpan.End() + wg.Done() + }() + + wg.Wait() + } + + query, queryArgs, scan := prepareMembershipsQuery(ctx, q.client, withOwnerRemoved, queries) eq := sq.Eq{membershipInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()} stmt, args, err := queries.toQuery(query).Where(eq).ToSql() if err != nil { return nil, errors.ThrowInvalidArgument(err, "QUERY-T84X9", "Errors.Query.InvalidRequest") } - latestSequence, err := q.latestSequence(ctx, orgMemberTable, instanceMemberTable, projectMemberTable, projectGrantMemberTable) + latestSequence, err := q.latestState(ctx, orgMemberTable, instanceMemberTable, projectMemberTable, projectGrantMemberTable) if err != nil { return nil, err } @@ -128,14 +161,14 @@ func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuer if err != nil { return nil, err } - memberships.LatestSequence = latestSequence + memberships.State = latestSequence return memberships, nil } var ( //membershipAlias is a hack to satisfy checks in the queries membershipAlias = table{ - name: "memberships", + name: "members", instanceIDCol: projection.MemberInstanceID, } membershipUserID = Column{ @@ -201,11 +234,11 @@ var ( } ) -func getMembershipFromQuery(withOwnerRemoved bool) (string, []interface{}) { - orgMembers, orgMembersArgs := prepareOrgMember(withOwnerRemoved) - iamMembers, iamMembersArgs := prepareIAMMember(withOwnerRemoved) - projectMembers, projectMembersArgs := prepareProjectMember(withOwnerRemoved) - projectGrantMembers, projectGrantMembersArgs := prepareProjectGrantMember(withOwnerRemoved) +func getMembershipFromQuery(withOwnerRemoved bool, queries *MembershipSearchQuery) (string, []interface{}) { + orgMembers, orgMembersArgs := prepareOrgMember(withOwnerRemoved, queries) + iamMembers, iamMembersArgs := prepareIAMMember(withOwnerRemoved, queries) + projectMembers, projectMembersArgs := prepareProjectMember(withOwnerRemoved, queries) + projectGrantMembers, projectGrantMembersArgs := prepareProjectGrantMember(withOwnerRemoved, queries) args := make([]interface{}, 0) args = append(append(append(append(args, orgMembersArgs...), iamMembersArgs...), projectMembersArgs...), projectGrantMembersArgs...) @@ -221,8 +254,8 @@ func getMembershipFromQuery(withOwnerRemoved bool) (string, []interface{}) { args } -func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerRemoved bool) (sq.SelectBuilder, []interface{}, func(*sql.Rows) (*Memberships, error)) { - query, args := getMembershipFromQuery(withOwnerRemoved) +func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerRemoved bool, queries *MembershipSearchQuery) (sq.SelectBuilder, []interface{}, func(*sql.Rows) (*Memberships, error)) { + query, args := getMembershipFromQuery(withOwnerRemoved, queries) return sq.Select( membershipUserID.identifier(), membershipRoles.identifier(), @@ -237,11 +270,13 @@ func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerR ProjectGrantColumnGrantedOrgID.identifier(), ProjectColumnName.identifier(), OrgColumnName.identifier(), + InstanceColumnName.identifier(), countColumn.identifier(), ).From(query). LeftJoin(join(ProjectColumnID, membershipProjectID)). LeftJoin(join(OrgColumnID, membershipOrgID)). - LeftJoin(join(ProjectGrantColumnGrantID, membershipGrantID) + db.Timetravel(call.Took(ctx))). + LeftJoin(join(ProjectGrantColumnGrantID, membershipGrantID)). + LeftJoin(join(InstanceColumnID, membershipInstanceID) + db.Timetravel(call.Took(ctx))). PlaceholderFormat(sq.Dollar), args, func(rows *sql.Rows) (*Memberships, error) { @@ -252,12 +287,13 @@ func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerR var ( membership = new(Membership) orgID = sql.NullString{} - iamID = sql.NullString{} + instanceID = sql.NullString{} projectID = sql.NullString{} grantID = sql.NullString{} grantedOrgID = sql.NullString{} projectName = sql.NullString{} orgName = sql.NullString{} + instanceName = sql.NullString{} ) err := rows.Scan( @@ -268,12 +304,13 @@ func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerR &membership.Sequence, &membership.ResourceOwner, &orgID, - &iamID, + &instanceID, &projectID, &grantID, &grantedOrgID, &projectName, &orgName, + &instanceName, &count, ) @@ -286,10 +323,10 @@ func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerR OrgID: orgID.String, Name: orgName.String, } - } else if iamID.Valid { + } else if instanceID.Valid { membership.IAM = &IAMMembership{ - IAMID: iamID.String, - Name: iamID.String, + IAMID: instanceID.String, + Name: instanceName.String, } } else if projectID.Valid && grantID.Valid && grantedOrgID.Valid { membership.ProjectGrant = &ProjectGrantMembership{ @@ -321,7 +358,7 @@ func prepareMembershipsQuery(ctx context.Context, db prepareDatabase, withOwnerR } } -func prepareOrgMember(withOwnerRemoved bool) (string, []interface{}) { +func prepareOrgMember(withOwnerRemoved bool, query *MembershipSearchQuery) (string, []interface{}) { builder := sq.Select( OrgMemberUserID.identifier(), OrgMemberRoles.identifier(), @@ -335,15 +372,23 @@ func prepareOrgMember(withOwnerRemoved bool) (string, []interface{}) { "NULL::TEXT AS "+membershipProjectID.name, "NULL::TEXT AS "+membershipGrantID.name, ).From(orgMemberTable.identifier()) + + for _, q := range query.Queries { + if q.Col().table.name == membershipAlias.name { + builder = q.toQuery(builder) + } + } + if !withOwnerRemoved { eq := sq.Eq{} addOrgMemberWithoutOwnerRemoved(eq) builder = builder.Where(eq) } + return builder.MustSql() } -func prepareIAMMember(withOwnerRemoved bool) (string, []interface{}) { +func prepareIAMMember(withOwnerRemoved bool, query *MembershipSearchQuery) (string, []interface{}) { builder := sq.Select( InstanceMemberUserID.identifier(), InstanceMemberRoles.identifier(), @@ -357,6 +402,13 @@ func prepareIAMMember(withOwnerRemoved bool) (string, []interface{}) { "NULL::TEXT AS "+membershipProjectID.name, "NULL::TEXT AS "+membershipGrantID.name, ).From(instanceMemberTable.identifier()) + + for _, q := range query.Queries { + if q.Col().table.name == membershipAlias.name { + builder = q.toQuery(builder) + } + } + if !withOwnerRemoved { eq := sq.Eq{} addIamMemberWithoutOwnerRemoved(eq) @@ -365,7 +417,7 @@ func prepareIAMMember(withOwnerRemoved bool) (string, []interface{}) { return builder.MustSql() } -func prepareProjectMember(withOwnerRemoved bool) (string, []interface{}) { +func prepareProjectMember(withOwnerRemoved bool, query *MembershipSearchQuery) (string, []interface{}) { builder := sq.Select( ProjectMemberUserID.identifier(), ProjectMemberRoles.identifier(), @@ -379,6 +431,13 @@ func prepareProjectMember(withOwnerRemoved bool) (string, []interface{}) { ProjectMemberProjectID.identifier(), "NULL::TEXT AS "+membershipGrantID.name, ).From(projectMemberTable.identifier()) + + for _, q := range query.Queries { + if q.Col().table.name == membershipAlias.name { + builder = q.toQuery(builder) + } + } + if !withOwnerRemoved { eq := sq.Eq{} addProjectMemberWithoutOwnerRemoved(eq) @@ -387,7 +446,7 @@ func prepareProjectMember(withOwnerRemoved bool) (string, []interface{}) { return builder.MustSql() } -func prepareProjectGrantMember(withOwnerRemoved bool) (string, []interface{}) { +func prepareProjectGrantMember(withOwnerRemoved bool, query *MembershipSearchQuery) (string, []interface{}) { builder := sq.Select( ProjectGrantMemberUserID.identifier(), ProjectGrantMemberRoles.identifier(), @@ -401,6 +460,13 @@ func prepareProjectGrantMember(withOwnerRemoved bool) (string, []interface{}) { ProjectGrantMemberProjectID.identifier(), ProjectGrantMemberGrantID.identifier(), ).From(projectGrantMemberTable.identifier()) + + for _, q := range query.Queries { + if q.Col().table.name == membershipAlias.name { + builder = q.toQuery(builder) + } + } + if !withOwnerRemoved { eq := sq.Eq{} addProjectGrantMemberWithoutOwnerRemoved(eq) diff --git a/internal/query/user_membership_test.go b/internal/query/user_membership_test.go index 108f5d860..c2453eda1 100644 --- a/internal/query/user_membership_test.go +++ b/internal/query/user_membership_test.go @@ -16,19 +16,20 @@ import ( var ( membershipsStmt = regexp.QuoteMeta( - "SELECT memberships.user_id" + - ", memberships.roles" + - ", memberships.creation_date" + - ", memberships.change_date" + - ", memberships.sequence" + - ", memberships.resource_owner" + - ", memberships.org_id" + - ", memberships.id" + - ", memberships.project_id" + - ", memberships.grant_id" + + "SELECT members.user_id" + + ", members.roles" + + ", members.creation_date" + + ", members.change_date" + + ", members.sequence" + + ", members.resource_owner" + + ", members.org_id" + + ", members.id" + + ", members.project_id" + + ", members.grant_id" + ", projections.project_grants3.granted_org_id" + ", projections.projects3.name" + ", projections.orgs1.name" + + ", projections.instances.name" + ", COUNT(*) OVER ()" + " FROM (" + "SELECT members.user_id" + @@ -86,10 +87,11 @@ var ( ", members.grant_id" + " FROM projections.project_grant_members3 AS members" + " WHERE members.granted_org_removed = $7 AND members.owner_removed = $8 AND members.user_owner_removed = $9" + - ") AS memberships" + - " LEFT JOIN projections.projects3 ON memberships.project_id = projections.projects3.id AND memberships.instance_id = projections.projects3.instance_id" + - " LEFT JOIN projections.orgs1 ON memberships.org_id = projections.orgs1.id AND memberships.instance_id = projections.orgs1.instance_id" + - " LEFT JOIN projections.project_grants3 ON memberships.grant_id = projections.project_grants3.grant_id AND memberships.instance_id = projections.project_grants3.instance_id" + + ") AS members" + + " LEFT JOIN projections.projects3 ON members.project_id = projections.projects3.id AND members.instance_id = projections.projects3.instance_id" + + " LEFT JOIN projections.orgs1 ON members.org_id = projections.orgs1.id AND members.instance_id = projections.orgs1.instance_id" + + " LEFT JOIN projections.project_grants3 ON members.grant_id = projections.project_grants3.grant_id AND members.instance_id = projections.project_grants3.instance_id" + + " LEFT JOIN projections.instances ON members.instance_id = projections.instances.id" + ` AS OF SYSTEM TIME '-1 ms'`) membershipCols = []string{ "user_id", @@ -105,6 +107,7 @@ var ( "granted_org_id", "name", //project name "name", //org name + "name", // instance name "count", } ) @@ -142,7 +145,7 @@ func Test_MembershipPrepares(t *testing.T) { [][]driver.Value{ { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -154,6 +157,7 @@ func Test_MembershipPrepares(t *testing.T) { nil, nil, "org-name", + nil, }, }, ), @@ -165,7 +169,7 @@ func Test_MembershipPrepares(t *testing.T) { Memberships: []*Membership{ { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -185,7 +189,7 @@ func Test_MembershipPrepares(t *testing.T) { [][]driver.Value{ { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -197,6 +201,7 @@ func Test_MembershipPrepares(t *testing.T) { nil, nil, nil, + "instance", }, }, ), @@ -208,12 +213,12 @@ func Test_MembershipPrepares(t *testing.T) { Memberships: []*Membership{ { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, ResourceOwner: "ro", - IAM: &IAMMembership{IAMID: "iam-id", Name: "iam-id"}, + IAM: &IAMMembership{IAMID: "iam-id", Name: "instance"}, }, }, }, @@ -228,7 +233,7 @@ func Test_MembershipPrepares(t *testing.T) { [][]driver.Value{ { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -240,6 +245,7 @@ func Test_MembershipPrepares(t *testing.T) { nil, "project-name", nil, + nil, }, }, ), @@ -251,7 +257,7 @@ func Test_MembershipPrepares(t *testing.T) { Memberships: []*Membership{ { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -271,7 +277,7 @@ func Test_MembershipPrepares(t *testing.T) { [][]driver.Value{ { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -283,6 +289,7 @@ func Test_MembershipPrepares(t *testing.T) { "granted-org-id", "project-name", nil, + nil, }, }, ), @@ -294,7 +301,7 @@ func Test_MembershipPrepares(t *testing.T) { Memberships: []*Membership{ { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -319,7 +326,7 @@ func Test_MembershipPrepares(t *testing.T) { [][]driver.Value{ { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -331,10 +338,11 @@ func Test_MembershipPrepares(t *testing.T) { nil, nil, "org-name", + nil, }, { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -346,10 +354,11 @@ func Test_MembershipPrepares(t *testing.T) { nil, nil, nil, + "instance", }, { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -361,10 +370,11 @@ func Test_MembershipPrepares(t *testing.T) { nil, "project-name", nil, + nil, }, { "user-id", - database.StringArray{"role1", "role2"}, + database.TextArray[string]{"role1", "role2"}, testNow, testNow, uint64(20211202), @@ -376,6 +386,7 @@ func Test_MembershipPrepares(t *testing.T) { "granted-org-id", "project-name", nil, + nil, }, }, ), @@ -387,7 +398,7 @@ func Test_MembershipPrepares(t *testing.T) { Memberships: []*Membership{ { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -396,16 +407,16 @@ func Test_MembershipPrepares(t *testing.T) { }, { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, ResourceOwner: "ro", - IAM: &IAMMembership{IAMID: "iam-id", Name: "iam-id"}, + IAM: &IAMMembership{IAMID: "iam-id", Name: "instance"}, }, { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -414,7 +425,7 @@ func Test_MembershipPrepares(t *testing.T) { }, { UserID: "user-id", - Roles: database.StringArray{"role1", "role2"}, + Roles: database.TextArray[string]{"role1", "role2"}, CreationDate: testNow, ChangeDate: testNow, Sequence: 20211202, @@ -456,7 +467,7 @@ func Test_MembershipPrepares(t *testing.T) { func prepareMembershipWrapper(withOwnerRemoved bool) func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*Memberships, error)) { return func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*Memberships, error)) { - builder, _, fun := prepareMembershipsQuery(ctx, db, withOwnerRemoved) + builder, _, fun := prepareMembershipsQuery(ctx, db, withOwnerRemoved, &MembershipSearchQuery{}) return builder, fun } } diff --git a/internal/query/user_metadata.go b/internal/query/user_metadata.go index 6a2be6a7e..6ee99735d 100644 --- a/internal/query/user_metadata.go +++ b/internal/query/user_metadata.go @@ -8,9 +8,12 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -82,7 +85,8 @@ func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bo defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserMetadataProjection.Trigger(ctx) + ctx, err = projection.UserMetadataProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := prepareUserMetadataQuery(ctx, q.client) @@ -114,7 +118,8 @@ func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.UserMetadataProjection.Trigger(ctx) + ctx, err = projection.UserMetadataProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := prepareUserMetadataListQuery(ctx, q.client) @@ -137,7 +142,7 @@ func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool if err != nil { return nil, err } - metadata.LatestSequence, err = q.latestSequence(ctx, userMetadataTable) + metadata.State, err = q.latestState(ctx, userMetadataTable) return metadata, err } diff --git a/internal/query/user_otp.go b/internal/query/user_otp.go index 955b7c924..77e5e16fd 100644 --- a/internal/query/user_otp.go +++ b/internal/query/user_otp.go @@ -80,6 +80,7 @@ func (wm *HumanOTPReadModel) Reduce() error { func (wm *HumanOTPReadModel) Query() *eventstore.SearchQueryBuilder { query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). AddQuery(). AggregateTypes(user.AggregateType). AggregateIDs(wm.AggregateID). diff --git a/internal/query/user_password.go b/internal/query/user_password.go index 9d8ab7140..0e9da8a5e 100644 --- a/internal/query/user_password.go +++ b/internal/query/user_password.go @@ -113,6 +113,7 @@ func (wm *HumanPasswordReadModel) Reduce() error { func (wm *HumanPasswordReadModel) Query() *eventstore.SearchQueryBuilder { query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). AllowTimeTravel(). AddQuery(). AggregateTypes(user.AggregateType). diff --git a/internal/query/user_personal_access_token.go b/internal/query/user_personal_access_token.go index 1024fae5b..e0a1fc90e 100644 --- a/internal/query/user_personal_access_token.go +++ b/internal/query/user_personal_access_token.go @@ -8,10 +8,13 @@ import ( sq "github.com/Masterminds/squirrel" + "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/call" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -77,7 +80,7 @@ type PersonalAccessToken struct { UserID string Expiration time.Time - Scopes database.StringArray + Scopes database.TextArray[string] } type PersonalAccessTokenSearchQueries struct { @@ -90,7 +93,8 @@ func (q *Queries) PersonalAccessTokenByID(ctx context.Context, shouldTriggerBulk defer func() { span.EndWithError(err) }() if shouldTriggerBulk { - ctx = projection.PersonalAccessTokenProjection.Trigger(ctx) + ctx, err = projection.PersonalAccessTokenProjection.Trigger(ctx, handler.WithAwaitRunning()) + logging.OnError(err).Debug("trigger failed") } query, scan := preparePersonalAccessTokenQuery(ctx, q.client) @@ -144,7 +148,7 @@ func (q *Queries) SearchPersonalAccessTokens(ctx context.Context, queries *Perso return nil, errors.ThrowInternal(err, "QUERY-Bmz63", "Errors.Internal") } - personalAccessTokens.LatestSequence, err = q.latestSequence(ctx, personalAccessTokensTable) + personalAccessTokens.State, err = q.latestState(ctx, personalAccessTokensTable) return personalAccessTokens, err } @@ -199,7 +203,7 @@ func preparePersonalAccessTokenQuery(ctx context.Context, db prepareDatabase) (s ) if err != nil { if errs.Is(err, sql.ErrNoRows) { - return nil, errors.ThrowNotFound(err, "QUERY-fk2fs", "Errors.PersonalAccessToken.NotFound") + return nil, errors.ThrowNotFound(err, "QUERY-fRunu", "Errors.PersonalAccessToken.NotFound") } return nil, errors.ThrowInternal(err, "QUERY-dj2FF", "Errors.Internal") } diff --git a/internal/query/user_personal_access_token_test.go b/internal/query/user_personal_access_token_test.go index c12a151ed..95b08c87c 100644 --- a/internal/query/user_personal_access_token_test.go +++ b/internal/query/user_personal_access_token_test.go @@ -104,7 +104,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { uint64(20211202), "user-id", time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - database.StringArray{"openid"}, + database.TextArray[string]{"openid"}, }, ), }, @@ -116,7 +116,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { Sequence: 20211202, UserID: "user-id", Expiration: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - Scopes: database.StringArray{"openid"}, + Scopes: database.TextArray[string]{"openid"}, }, }, { @@ -164,7 +164,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { uint64(20211202), "user-id", time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - database.StringArray{"openid"}, + database.TextArray[string]{"openid"}, }, }, ), @@ -182,7 +182,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { Sequence: 20211202, UserID: "user-id", Expiration: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - Scopes: database.StringArray{"openid"}, + Scopes: database.TextArray[string]{"openid"}, }, }, }, @@ -203,7 +203,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { uint64(20211202), "user-id", time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - database.StringArray{"openid"}, + database.TextArray[string]{"openid"}, }, { "token-id2", @@ -213,7 +213,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { uint64(20211202), "user-id", time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - database.StringArray{"openid"}, + database.TextArray[string]{"openid"}, }, }, ), @@ -231,7 +231,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { Sequence: 20211202, UserID: "user-id", Expiration: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - Scopes: database.StringArray{"openid"}, + Scopes: database.TextArray[string]{"openid"}, }, { ID: "token-id2", @@ -241,7 +241,7 @@ func Test_PersonalAccessTokenPrepares(t *testing.T) { Sequence: 20211202, UserID: "user-id", Expiration: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), - Scopes: database.StringArray{"openid"}, + Scopes: database.TextArray[string]{"openid"}, }, }, }, diff --git a/internal/query/user_test.go b/internal/query/user_test.go index 986d3c1e9..866cab0ca 100644 --- a/internal/query/user_test.go +++ b/internal/query/user_test.go @@ -363,7 +363,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeHuman, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human "id", @@ -397,7 +397,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeHuman, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", Human: &Human{ FirstName: "first_name", @@ -430,7 +430,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeMachine, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human nil, @@ -464,7 +464,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeMachine, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", Machine: &Machine{ Name: "name", @@ -873,7 +873,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeHuman, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human "id", @@ -904,7 +904,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeHuman, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", FirstName: "first_name", LastName: "last_name", @@ -936,7 +936,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeHuman, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human "id", @@ -1017,7 +1017,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeHuman, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human "id", @@ -1056,7 +1056,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeHuman, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", Human: &Human{ FirstName: "first_name", @@ -1092,7 +1092,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeHuman, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human "id", @@ -1123,7 +1123,7 @@ func Test_UserPrepares(t *testing.T) { domain.UserStateActive, domain.UserTypeMachine, "username", - database.StringArray{"login_name1", "login_name2"}, + database.TextArray[string]{"login_name1", "login_name2"}, "login_name1", //human nil, @@ -1162,7 +1162,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeHuman, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", Human: &Human{ FirstName: "first_name", @@ -1187,7 +1187,7 @@ func Test_UserPrepares(t *testing.T) { State: domain.UserStateActive, Type: domain.UserTypeMachine, Username: "username", - LoginNames: database.StringArray{"login_name1", "login_name2"}, + LoginNames: database.TextArray[string]{"login_name1", "login_name2"}, PreferredLoginName: "login_name1", Machine: &Machine{ Name: "name", diff --git a/internal/query/zitadel_permission.go b/internal/query/zitadel_permission.go index b44ac4d85..29baa7680 100644 --- a/internal/query/zitadel_permission.go +++ b/internal/query/zitadel_permission.go @@ -26,7 +26,7 @@ func (q *Queries) MyZitadelPermissions(ctx context.Context, orgID, userID string } memberships, err := q.Memberships(ctx, &MembershipSearchQuery{ Queries: []SearchQuery{userIDQuery, Or(orgIDsQuery, grantedOrgIDQuery)}, - }, false) + }, false, false) if err != nil { return nil, err } diff --git a/internal/renderer/renderer.go b/internal/renderer/renderer.go index ceeacc03c..72da287a3 100644 --- a/internal/renderer/renderer.go +++ b/internal/renderer/renderer.go @@ -116,10 +116,8 @@ func (r *Renderer) addFileToTemplate(dir http.FileSystem, tmpl *template.Templat return err } tmpl, err = tmpl.New(file.Name()).Funcs(funcs).Parse(string(content)) - if err != nil { - return err - } - return nil + + return err } func (r *Renderer) registerTranslateFn(req *http.Request, translator *i18n.Translator, funcs map[string]interface{}) map[string]interface{} { diff --git a/internal/repository/action/action.go b/internal/repository/action/action.go index 38eaf29e3..e1195fa66 100644 --- a/internal/repository/action/action.go +++ b/internal/repository/action/action.go @@ -2,12 +2,10 @@ package action import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -20,15 +18,15 @@ const ( RemovedEventType = eventTypePrefix + "removed" ) -func NewAddActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint { +func NewAddActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueActionNameType, actionName+":"+resourceOwner, "Errors.Action.AlreadyExists") } -func NewRemoveActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueActionNameType, actionName+":"+resourceOwner) } @@ -42,12 +40,12 @@ type AddedEvent struct { AllowedToFail bool `json:"allowedToFail"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddActionNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddActionNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} } func NewAddedEvent( @@ -71,12 +69,12 @@ func NewAddedEvent( } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action added") } @@ -94,15 +92,15 @@ type ChangedEvent struct { oldName string } -func (e *ChangedEvent) Data() interface{} { +func (e *ChangedEvent) Payload() interface{} { return e } -func (e *ChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { if e.oldName == "" { return nil } - return []*eventstore.EventUniqueConstraint{ + return []*eventstore.UniqueConstraint{ NewRemoveActionNameUniqueConstraint(e.oldName, e.Aggregate().ResourceOwner), NewAddActionNameUniqueConstraint(*e.Name, e.Aggregate().ResourceOwner), } @@ -156,12 +154,12 @@ func ChangeAllowedToFail(allowedToFail bool) func(event *ChangedEvent) { } } -func ChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action changed") } @@ -173,11 +171,11 @@ type DeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *DeactivatedEvent) Data() interface{} { +func (e *DeactivatedEvent) Payload() interface{} { return nil } -func (e *DeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -191,7 +189,7 @@ func NewDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) * } } -func DeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &DeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -201,11 +199,11 @@ type ReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *ReactivatedEvent) Data() interface{} { +func (e *ReactivatedEvent) Payload() interface{} { return nil } -func (e *ReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -219,7 +217,7 @@ func NewReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) * } } -func ReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &ReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -231,12 +229,12 @@ type RemovedEvent struct { name string } -func (e *RemovedEvent) Data() interface{} { +func (e *RemovedEvent) Payload() interface{} { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveActionNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveActionNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} } func NewRemovedEvent( @@ -254,7 +252,7 @@ func NewRemovedEvent( } } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/asset/asset.go b/internal/repository/asset/asset.go index 344585fb3..e965b1ef8 100644 --- a/internal/repository/asset/asset.go +++ b/internal/repository/asset/asset.go @@ -1,11 +1,8 @@ package asset import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -19,11 +16,11 @@ type AddedEvent struct { StoreKey string `json:"storeKey"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -38,12 +35,12 @@ func NewAddedEvent( } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "ASSET-1WEAx", "unable to unmarshal asset") } @@ -57,11 +54,11 @@ type RemovedEvent struct { StoreKey string `json:"storeKey"` } -func (e *RemovedEvent) Data() interface{} { +func (e *RemovedEvent) Payload() interface{} { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -76,12 +73,12 @@ func NewRemovedEvent( } } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "ASSET-1m9PP", "unable to unmarshal asset") } diff --git a/internal/repository/authrequest/auth_request.go b/internal/repository/authrequest/auth_request.go index a279f23c9..3633da54a 100644 --- a/internal/repository/authrequest/auth_request.go +++ b/internal/repository/authrequest/auth_request.go @@ -2,13 +2,11 @@ package authrequest import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -40,11 +38,11 @@ type AddedEvent struct { HintUserID *string `json:"hint_user_id,omitempty"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -88,11 +86,11 @@ func NewAddedEvent(ctx context.Context, } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "AUTHR-DG4gn", "unable to unmarshal auth request added") } @@ -109,11 +107,11 @@ type SessionLinkedEvent struct { AuthMethods []domain.UserAuthMethodType `json:"auth_methods"` } -func (e *SessionLinkedEvent) Data() interface{} { +func (e *SessionLinkedEvent) Payload() interface{} { return e } -func (e *SessionLinkedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SessionLinkedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -137,11 +135,11 @@ func NewSessionLinkedEvent(ctx context.Context, } } -func SessionLinkedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SessionLinkedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &SessionLinkedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "AUTHR-Sfe3w", "unable to unmarshal auth request session linked") } @@ -155,11 +153,11 @@ type FailedEvent struct { Reason domain.OIDCErrorReason `json:"reason,omitempty"` } -func (e *FailedEvent) Data() interface{} { +func (e *FailedEvent) Payload() interface{} { return e } -func (e *FailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *FailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -178,11 +176,11 @@ func NewFailedEvent( } } -func FailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func FailedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &FailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "AUTHR-Sfe3w", "unable to unmarshal auth request session linked") } @@ -194,11 +192,11 @@ type CodeAddedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *CodeAddedEvent) Data() interface{} { +func (e *CodeAddedEvent) Payload() interface{} { return e } -func (e *CodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -214,11 +212,11 @@ func NewCodeAddedEvent(ctx context.Context, } } -func CodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &CodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "AUTHR-Sfe3w", "unable to unmarshal auth request code added") } @@ -230,11 +228,11 @@ type CodeExchangedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *CodeExchangedEvent) Data() interface{} { +func (e *CodeExchangedEvent) Payload() interface{} { return nil } -func (e *CodeExchangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CodeExchangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -250,7 +248,7 @@ func NewCodeExchangedEvent(ctx context.Context, } } -func CodeExchangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CodeExchangedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &CodeExchangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -260,11 +258,11 @@ type SucceededEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *SucceededEvent) Data() interface{} { +func (e *SucceededEvent) Payload() interface{} { return nil } -func (e *SucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -280,7 +278,7 @@ func NewSucceededEvent(ctx context.Context, } } -func SucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func SucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { return &SucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/deviceauth/constraints.go b/internal/repository/deviceauth/constraints.go index 679220524..d8362760e 100644 --- a/internal/repository/deviceauth/constraints.go +++ b/internal/repository/deviceauth/constraints.go @@ -17,8 +17,8 @@ func deviceCodeUniqueField(clientID, deviceCode string) string { return strings.Join([]string{clientID, deviceCode}, ":") } -func NewAddUniqueConstraints(clientID, deviceCode, userCode string) []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ +func NewAddUniqueConstraints(clientID, deviceCode, userCode string) []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ eventstore.NewAddEventUniqueConstraint( UniqueDeviceCode, deviceCodeUniqueField(clientID, deviceCode), @@ -32,13 +32,13 @@ func NewAddUniqueConstraints(clientID, deviceCode, userCode string) []*eventstor } } -func NewRemoveUniqueConstraints(clientID, deviceCode, userCode string) []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ - eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveUniqueConstraints(clientID, deviceCode, userCode string) []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ + eventstore.NewRemoveUniqueConstraint( UniqueDeviceCode, deviceCodeUniqueField(clientID, deviceCode), ), - eventstore.NewRemoveEventUniqueConstraint( + eventstore.NewRemoveUniqueConstraint( UniqueUserCode, userCode, ), diff --git a/internal/repository/deviceauth/device_auth.go b/internal/repository/deviceauth/device_auth.go index 0ece3e78f..b03f79740 100644 --- a/internal/repository/deviceauth/device_auth.go +++ b/internal/repository/deviceauth/device_auth.go @@ -31,11 +31,11 @@ func (e *AddedEvent) SetBaseEvent(b *eventstore.BaseEvent) { e.BaseEvent = b } -func (e *AddedEvent) Data() any { +func (e *AddedEvent) Payload() any { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return NewAddUniqueConstraints(e.ClientID, e.DeviceCode, e.UserCode) } @@ -65,11 +65,11 @@ func (e *ApprovedEvent) SetBaseEvent(b *eventstore.BaseEvent) { e.BaseEvent = b } -func (e *ApprovedEvent) Data() any { +func (e *ApprovedEvent) Payload() any { return e } -func (e *ApprovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ApprovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -95,11 +95,11 @@ func (e *CanceledEvent) SetBaseEvent(b *eventstore.BaseEvent) { e.BaseEvent = b } -func (e *CanceledEvent) Data() any { +func (e *CanceledEvent) Payload() any { return e } -func (e *CanceledEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CanceledEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -119,11 +119,11 @@ func (e *RemovedEvent) SetBaseEvent(b *eventstore.BaseEvent) { e.BaseEvent = b } -func (e *RemovedEvent) Data() any { +func (e *RemovedEvent) Payload() any { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return NewRemoveUniqueConstraints(e.ClientID, e.DeviceCode, e.UserCode) } diff --git a/internal/repository/feature/feature.go b/internal/repository/feature/feature.go index b3757c103..4ce007d2b 100644 --- a/internal/repository/feature/feature.go +++ b/internal/repository/feature/feature.go @@ -26,11 +26,11 @@ func (e *SetEvent[T]) SetBaseEvent(b *eventstore.BaseEvent) { e.BaseEvent = b } -func (e *SetEvent[T]) Data() interface{} { +func (e *SetEvent[T]) Payload() interface{} { return e } -func (e *SetEvent[T]) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SetEvent[T]) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/flow/flow.go b/internal/repository/flow/flow.go index 649cab70f..2bf0932ec 100644 --- a/internal/repository/flow/flow.go +++ b/internal/repository/flow/flow.go @@ -1,12 +1,9 @@ package flow import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -25,11 +22,11 @@ type TriggerActionsSetEvent struct { ActionIDs []string `json:"actionIDs"` } -func (e *TriggerActionsSetEvent) Data() interface{} { +func (e *TriggerActionsSetEvent) Payload() interface{} { return e } -func (e *TriggerActionsSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *TriggerActionsSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -47,12 +44,12 @@ func NewTriggerActionsSetEvent( } } -func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func TriggerActionsSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &TriggerActionsSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "FLOW-4n8vs", "unable to unmarshal trigger actions") } @@ -68,11 +65,11 @@ type TriggerActionsCascadeRemovedEvent struct { ActionID string `json:"actionID"` } -func (e *TriggerActionsCascadeRemovedEvent) Data() interface{} { +func (e *TriggerActionsCascadeRemovedEvent) Payload() interface{} { return e } -func (e *TriggerActionsCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *TriggerActionsCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -88,12 +85,12 @@ func NewTriggerActionsCascadeRemovedEvent( } } -func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func TriggerActionsCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &TriggerActionsCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "FLOW-4n8vs", "unable to unmarshal trigger actions") } @@ -107,11 +104,11 @@ type FlowClearedEvent struct { FlowType domain.FlowType `json:"flowType"` } -func (e *FlowClearedEvent) Data() interface{} { +func (e *FlowClearedEvent) Payload() interface{} { return e } -func (e *FlowClearedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *FlowClearedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -125,12 +122,12 @@ func NewFlowClearedEvent( } } -func FlowClearedEventMapper(event *repository.Event) (eventstore.Event, error) { +func FlowClearedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &FlowClearedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "FLOW-BHfg2", "unable to unmarshal flow cleared") } diff --git a/internal/repository/idp/apple.go b/internal/repository/idp/apple.go index 8c9972c54..f140357c0 100644 --- a/internal/repository/idp/apple.go +++ b/internal/repository/idp/apple.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type AppleIDPAddedEvent struct { @@ -46,20 +43,20 @@ func NewAppleIDPAddedEvent( } } -func (e *AppleIDPAddedEvent) Data() interface{} { +func (e *AppleIDPAddedEvent) Payload() interface{} { return e } -func (e *AppleIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AppleIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func AppleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AppleIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Beqss", "unable to unmarshal event") } @@ -142,20 +139,20 @@ func ChangeAppleOptions(options OptionChanges) func(*AppleIDPChangedEvent) { } } -func (e *AppleIDPChangedEvent) Data() interface{} { +func (e *AppleIDPChangedEvent) Payload() interface{} { return e } -func (e *AppleIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AppleIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func AppleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AppleIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-NBe1s", "unable to unmarshal event") } diff --git a/internal/repository/idp/azuread.go b/internal/repository/idp/azuread.go index e986801f2..ae7a434a8 100644 --- a/internal/repository/idp/azuread.go +++ b/internal/repository/idp/azuread.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type AzureADIDPAddedEvent struct { @@ -46,20 +43,20 @@ func NewAzureADIDPAddedEvent( } } -func (e *AzureADIDPAddedEvent) Data() interface{} { +func (e *AzureADIDPAddedEvent) Payload() interface{} { return e } -func (e *AzureADIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AzureADIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AzureADIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Grh2g", "unable to unmarshal event") } @@ -142,20 +139,20 @@ func ChangeAzureADIsEmailVerified(isEmailVerified bool) func(*AzureADIDPChangedE } } -func (e *AzureADIDPChangedEvent) Data() interface{} { +func (e *AzureADIDPChangedEvent) Payload() interface{} { return e } -func (e *AzureADIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AzureADIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AzureADIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event") } diff --git a/internal/repository/idp/github.go b/internal/repository/idp/github.go index e3f30f20f..0b0a902a1 100644 --- a/internal/repository/idp/github.go +++ b/internal/repository/idp/github.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type GitHubIDPAddedEvent struct { @@ -40,20 +37,20 @@ func NewGitHubIDPAddedEvent( } } -func (e *GitHubIDPAddedEvent) Data() interface{} { +func (e *GitHubIDPAddedEvent) Payload() interface{} { return e } -func (e *GitHubIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitHubIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitHubIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-sdfs3", "unable to unmarshal event") } @@ -121,20 +118,20 @@ func ChangeGitHubScopes(scopes []string) func(*GitHubIDPChangedEvent) { } } -func (e *GitHubIDPChangedEvent) Data() interface{} { +func (e *GitHubIDPChangedEvent) Payload() interface{} { return e } -func (e *GitHubIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitHubIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitHubIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Sfrth", "unable to unmarshal event") } @@ -182,20 +179,20 @@ func NewGitHubEnterpriseIDPAddedEvent( } } -func (e *GitHubEnterpriseIDPAddedEvent) Data() interface{} { +func (e *GitHubEnterpriseIDPAddedEvent) Payload() interface{} { return e } -func (e *GitHubEnterpriseIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitHubEnterpriseIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitHubEnterpriseIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-sdfs3", "unable to unmarshal event") } @@ -284,20 +281,20 @@ func ChangeGitHubEnterpriseScopes(scopes []string) func(*GitHubEnterpriseIDPChan } } -func (e *GitHubEnterpriseIDPChangedEvent) Data() interface{} { +func (e *GitHubEnterpriseIDPChangedEvent) Payload() interface{} { return e } -func (e *GitHubEnterpriseIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitHubEnterpriseIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitHubEnterpriseIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-ASf3r", "unable to unmarshal event") } diff --git a/internal/repository/idp/gitlab.go b/internal/repository/idp/gitlab.go index 78ef99c4b..85cfabc74 100644 --- a/internal/repository/idp/gitlab.go +++ b/internal/repository/idp/gitlab.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type GitLabIDPAddedEvent struct { @@ -40,20 +37,20 @@ func NewGitLabIDPAddedEvent( } } -func (e *GitLabIDPAddedEvent) Data() interface{} { +func (e *GitLabIDPAddedEvent) Payload() interface{} { return e } -func (e *GitLabIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitLabIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitLabIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-KLewio", "unable to unmarshal event") } @@ -122,20 +119,20 @@ func ChangeGitLabOptions(options OptionChanges) func(*GitLabIDPChangedEvent) { } } -func (e *GitLabIDPChangedEvent) Data() interface{} { +func (e *GitLabIDPChangedEvent) Payload() interface{} { return e } -func (e *GitLabIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitLabIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitLabIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Sfhjk", "unable to unmarshal event") } @@ -177,20 +174,20 @@ func NewGitLabSelfHostedIDPAddedEvent( } } -func (e *GitLabSelfHostedIDPAddedEvent) Data() interface{} { +func (e *GitLabSelfHostedIDPAddedEvent) Payload() interface{} { return e } -func (e *GitLabSelfHostedIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitLabSelfHostedIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitLabSelfHostedIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-S1efv", "unable to unmarshal event") } @@ -266,20 +263,20 @@ func ChangeGitLabSelfHostedOptions(options OptionChanges) func(*GitLabSelfHosted } } -func (e *GitLabSelfHostedIDPChangedEvent) Data() interface{} { +func (e *GitLabSelfHostedIDPChangedEvent) Payload() interface{} { return e } -func (e *GitLabSelfHostedIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GitLabSelfHostedIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GitLabSelfHostedIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-SFrhj", "unable to unmarshal event") } diff --git a/internal/repository/idp/google.go b/internal/repository/idp/google.go index 4bb427ac8..3510b27ac 100644 --- a/internal/repository/idp/google.go +++ b/internal/repository/idp/google.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type GoogleIDPAddedEvent struct { @@ -40,20 +37,20 @@ func NewGoogleIDPAddedEvent( } } -func (e *GoogleIDPAddedEvent) Data() interface{} { +func (e *GoogleIDPAddedEvent) Payload() interface{} { return e } -func (e *GoogleIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GoogleIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GoogleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GoogleIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-SAff1", "unable to unmarshal event") } @@ -121,20 +118,20 @@ func ChangeGoogleOptions(options OptionChanges) func(*GoogleIDPChangedEvent) { } } -func (e *GoogleIDPChangedEvent) Data() interface{} { +func (e *GoogleIDPChangedEvent) Payload() interface{} { return e } -func (e *GoogleIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GoogleIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func GoogleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GoogleIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-SF3t2", "unable to unmarshal event") } diff --git a/internal/repository/idp/idp.go b/internal/repository/idp/idp.go index 540e2d243..96b220485 100644 --- a/internal/repository/idp/idp.go +++ b/internal/repository/idp/idp.go @@ -1,11 +1,8 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type Options struct { @@ -74,20 +71,20 @@ func NewRemovedEvent( } } -func (e *RemovedEvent) Data() interface{} { +func (e *RemovedEvent) Payload() interface{} { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-plSD2", "unable to unmarshal event") } diff --git a/internal/repository/idp/jwt.go b/internal/repository/idp/jwt.go index 076c10909..786d4a62f 100644 --- a/internal/repository/idp/jwt.go +++ b/internal/repository/idp/jwt.go @@ -1,11 +1,8 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type JWTIDPAddedEvent struct { @@ -42,20 +39,20 @@ func NewJWTIDPAddedEvent( } } -func (e *JWTIDPAddedEvent) Data() interface{} { +func (e *JWTIDPAddedEvent) Payload() interface{} { return e } -func (e *JWTIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *JWTIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &JWTIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Et1dq", "unable to unmarshal event") } @@ -131,20 +128,20 @@ func ChangeJWTOptions(options OptionChanges) func(*JWTIDPChangedEvent) { } } -func (e *JWTIDPChangedEvent) Data() interface{} { +func (e *JWTIDPChangedEvent) Payload() interface{} { return e } -func (e *JWTIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *JWTIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &JWTIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event") } diff --git a/internal/repository/idp/ldap.go b/internal/repository/idp/ldap.go index 5115bc46a..bc1b41fb7 100644 --- a/internal/repository/idp/ldap.go +++ b/internal/repository/idp/ldap.go @@ -1,13 +1,11 @@ package idp import ( - "encoding/json" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type LDAPIDPAddedEvent struct { @@ -165,20 +163,20 @@ func NewLDAPIDPAddedEvent( } } -func (e *LDAPIDPAddedEvent) Data() interface{} { +func (e *LDAPIDPAddedEvent) Payload() interface{} { return e } -func (e *LDAPIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LDAPIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func LDAPIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LDAPIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Dgh42", "unable to unmarshal event") } @@ -329,20 +327,20 @@ func ChangeLDAPOptions(options OptionChanges) func(*LDAPIDPChangedEvent) { } } -func (e *LDAPIDPChangedEvent) Data() interface{} { +func (e *LDAPIDPChangedEvent) Payload() interface{} { return e } -func (e *LDAPIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LDAPIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func LDAPIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LDAPIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Sfth3", "unable to unmarshal event") } diff --git a/internal/repository/idp/oauth.go b/internal/repository/idp/oauth.go index b693cd25e..ce0178f8d 100644 --- a/internal/repository/idp/oauth.go +++ b/internal/repository/idp/oauth.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type OAuthIDPAddedEvent struct { @@ -52,20 +49,20 @@ func NewOAuthIDPAddedEvent( } } -func (e *OAuthIDPAddedEvent) Data() interface{} { +func (e *OAuthIDPAddedEvent) Payload() interface{} { return e } -func (e *OAuthIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OAuthIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OAuthIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OAuthIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Et1dq", "unable to unmarshal event") } @@ -161,20 +158,20 @@ func ChangeOAuthIDAttribute(idAttribute string) func(*OAuthIDPChangedEvent) { } } -func (e *OAuthIDPChangedEvent) Data() interface{} { +func (e *OAuthIDPChangedEvent) Payload() interface{} { return e } -func (e *OAuthIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OAuthIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OAuthIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OAuthIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-SAf3gw", "unable to unmarshal event") } diff --git a/internal/repository/idp/oidc.go b/internal/repository/idp/oidc.go index 09437d18e..45478dc83 100644 --- a/internal/repository/idp/oidc.go +++ b/internal/repository/idp/oidc.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type OIDCIDPAddedEvent struct { @@ -46,20 +43,20 @@ func NewOIDCIDPAddedEvent( } } -func (e *OIDCIDPAddedEvent) Data() interface{} { +func (e *OIDCIDPAddedEvent) Payload() interface{} { return e } -func (e *OIDCIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Et1dq", "unable to unmarshal event") } @@ -142,20 +139,20 @@ func ChangeOIDCIsIDTokenMapping(idTokenMapping bool) func(*OIDCIDPChangedEvent) } } -func (e *OIDCIDPChangedEvent) Data() interface{} { +func (e *OIDCIDPChangedEvent) Payload() interface{} { return e } -func (e *OIDCIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event") } @@ -197,11 +194,11 @@ func (e *OIDCIDPMigratedAzureADEvent) Data() interface{} { return e } -func (e *OIDCIDPMigratedAzureADEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCIDPMigratedAzureADEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedAzureADEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := AzureADIDPAddedEventMapper(event) if err != nil { return nil, err @@ -240,11 +237,11 @@ func (e *OIDCIDPMigratedGoogleEvent) Data() interface{} { return e } -func (e *OIDCIDPMigratedGoogleEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCIDPMigratedGoogleEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedGoogleEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := GoogleIDPAddedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/idp/saml.go b/internal/repository/idp/saml.go index 2030bcd6f..c4205600f 100644 --- a/internal/repository/idp/saml.go +++ b/internal/repository/idp/saml.go @@ -1,12 +1,9 @@ package idp import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type SAMLIDPAddedEvent struct { @@ -46,20 +43,20 @@ func NewSAMLIDPAddedEvent( } } -func (e *SAMLIDPAddedEvent) Data() interface{} { +func (e *SAMLIDPAddedEvent) Payload() interface{} { return e } -func (e *SAMLIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SAMLIDPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SAMLIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLIDPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-v9uajo3k71", "unable to unmarshal event") } @@ -142,20 +139,20 @@ func ChangeSAMLOptions(options OptionChanges) func(*SAMLIDPChangedEvent) { } } -func (e *SAMLIDPChangedEvent) Data() interface{} { +func (e *SAMLIDPChangedEvent) Payload() interface{} { return e } -func (e *SAMLIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SAMLIDPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SAMLIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLIDPChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-w1t1824tw5", "unable to unmarshal event") } diff --git a/internal/repository/idpconfig/idp_config.go b/internal/repository/idpconfig/idp_config.go index 1578ed828..ca9754ebe 100644 --- a/internal/repository/idpconfig/idp_config.go +++ b/internal/repository/idpconfig/idp_config.go @@ -1,27 +1,24 @@ package idpconfig import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( UniqueIDPConfigNameType = "idp_config_names" ) -func NewAddIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string) *eventstore.EventUniqueConstraint { +func NewAddIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueIDPConfigNameType, idpConfigName+resourceOwner, "Errors.IDPConfig.AlreadyExists") } -func NewRemoveIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueIDPConfigNameType, idpConfigName+resourceOwner) } @@ -54,20 +51,20 @@ func NewIDPConfigAddedEvent( } } -func (e *IDPConfigAddedEvent) Data() interface{} { +func (e *IDPConfigAddedEvent) Payload() interface{} { return e } -func (e *IDPConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddIDPConfigNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} +func (e *IDPConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddIDPConfigNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} } -func IDPConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IDPConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } @@ -85,15 +82,15 @@ type IDPConfigChangedEvent struct { oldName string `json:"-"` } -func (e *IDPConfigChangedEvent) Data() interface{} { +func (e *IDPConfigChangedEvent) Payload() interface{} { return e } -func (e *IDPConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IDPConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { if e.oldName == "" { return nil } - return []*eventstore.EventUniqueConstraint{ + return []*eventstore.UniqueConstraint{ NewRemoveIDPConfigNameUniqueConstraint(e.oldName, e.Aggregate().ResourceOwner), NewAddIDPConfigNameUniqueConstraint(*e.Name, e.Aggregate().ResourceOwner), } @@ -139,12 +136,12 @@ func ChangeAutoRegister(autoRegister bool) func(*IDPConfigChangedEvent) { } } -func IDPConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IDPConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } @@ -169,20 +166,20 @@ func NewIDPConfigDeactivatedEvent( } } -func (e *IDPConfigDeactivatedEvent) Data() interface{} { +func (e *IDPConfigDeactivatedEvent) Payload() interface{} { return e } -func (e *IDPConfigDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IDPConfigDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func IDPConfigDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IDPConfigDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } @@ -207,20 +204,20 @@ func NewIDPConfigReactivatedEvent( } } -func (e *IDPConfigReactivatedEvent) Data() interface{} { +func (e *IDPConfigReactivatedEvent) Payload() interface{} { return e } -func (e *IDPConfigReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IDPConfigReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func IDPConfigReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IDPConfigReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } @@ -248,20 +245,20 @@ func NewIDPConfigRemovedEvent( } } -func (e *IDPConfigRemovedEvent) Data() interface{} { +func (e *IDPConfigRemovedEvent) Payload() interface{} { return e } -func (e *IDPConfigRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveIDPConfigNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} +func (e *IDPConfigRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveIDPConfigNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} } -func IDPConfigRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IDPConfigRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } diff --git a/internal/repository/idpconfig/jwt_config.go b/internal/repository/idpconfig/jwt_config.go index e329ff391..b2c460fdb 100644 --- a/internal/repository/idpconfig/jwt_config.go +++ b/internal/repository/idpconfig/jwt_config.go @@ -1,12 +1,9 @@ package idpconfig import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -24,11 +21,11 @@ type JWTConfigAddedEvent struct { HeaderName string `json:"headerName,omitempty"` } -func (e *JWTConfigAddedEvent) Data() interface{} { +func (e *JWTConfigAddedEvent) Payload() interface{} { return e } -func (e *JWTConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *JWTConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -50,12 +47,12 @@ func NewJWTConfigAddedEvent( } } -func JWTConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &JWTConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "JWT-m0fwf", "unable to unmarshal event") } @@ -74,11 +71,11 @@ type JWTConfigChangedEvent struct { HeaderName *string `json:"headerName,omitempty"` } -func (e *JWTConfigChangedEvent) Data() interface{} { +func (e *JWTConfigChangedEvent) Payload() interface{} { return e } -func (e *JWTConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *JWTConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -126,12 +123,12 @@ func ChangeHeaderName(headerName string) func(*JWTConfigChangedEvent) { } } -func JWTConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &JWTConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "JWT-fk3fs", "unable to unmarshal event") } diff --git a/internal/repository/idpconfig/oidc_config.go b/internal/repository/idpconfig/oidc_config.go index 096c55c9d..36e0136f0 100644 --- a/internal/repository/idpconfig/oidc_config.go +++ b/internal/repository/idpconfig/oidc_config.go @@ -1,14 +1,10 @@ package idpconfig import ( - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -31,11 +27,11 @@ type OIDCConfigAddedEvent struct { UserNameMapping domain.OIDCMappingField `json:"usernameMapping,omitempty"` } -func (e *OIDCConfigAddedEvent) Data() interface{} { +func (e *OIDCConfigAddedEvent) Payload() interface{} { return e } -func (e *OIDCConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -66,12 +62,12 @@ func NewOIDCConfigAddedEvent( } } -func OIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } @@ -95,11 +91,11 @@ type OIDCConfigChangedEvent struct { UserNameMapping *domain.OIDCMappingField `json:"usernameMapping,omitempty"` } -func (e *OIDCConfigChangedEvent) Data() interface{} { +func (e *OIDCConfigChangedEvent) Payload() interface{} { return e } -func (e *OIDCConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -171,12 +167,12 @@ func ChangeScopes(scopes []string) func(*OIDCConfigChangedEvent) { } } -func OIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-plaBZ", "unable to unmarshal event") } diff --git a/internal/repository/idpintent/intent.go b/internal/repository/idpintent/intent.go index 39779df4d..7a596becb 100644 --- a/internal/repository/idpintent/intent.go +++ b/internal/repository/idpintent/intent.go @@ -2,13 +2,11 @@ package idpintent import ( "context" - "encoding/json" "net/url" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -47,20 +45,20 @@ func NewStartedEvent( } } -func (e *StartedEvent) Data() interface{} { +func (e *StartedEvent) Payload() any { return e } -func (e *StartedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *StartedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func StartedEventMapper(event *repository.Event) (eventstore.Event, error) { +func StartedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &StartedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Sf3f1", "unable to unmarshal event") } @@ -105,20 +103,20 @@ func NewSucceededEvent( } } -func (e *SucceededEvent) Data() interface{} { +func (e *SucceededEvent) Payload() interface{} { return e } -func (e *SucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func SucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-HBreq", "unable to unmarshal event") } @@ -160,20 +158,20 @@ func NewSAMLSucceededEvent( } } -func (e *SAMLSucceededEvent) Data() interface{} { +func (e *SAMLSucceededEvent) Payload() interface{} { return e } -func (e *SAMLSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SAMLSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SAMLSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-l4tw23y6lq", "unable to unmarshal event") } @@ -202,20 +200,20 @@ func NewSAMLRequestEvent( } } -func (e *SAMLRequestEvent) Data() interface{} { +func (e *SAMLRequestEvent) Payload() interface{} { return e } -func (e *SAMLRequestEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SAMLRequestEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SAMLRequestEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLRequestEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLRequestEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-l85678vwlf", "unable to unmarshal event") } @@ -257,20 +255,20 @@ func NewLDAPSucceededEvent( } } -func (e *LDAPSucceededEvent) Data() interface{} { +func (e *LDAPSucceededEvent) Payload() interface{} { return e } -func (e *LDAPSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LDAPSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func LDAPSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LDAPSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-HBreq", "unable to unmarshal event") } @@ -299,20 +297,20 @@ func NewFailedEvent( } } -func (e *FailedEvent) Data() interface{} { +func (e *FailedEvent) Payload() interface{} { return e } -func (e *FailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *FailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func FailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func FailedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &FailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IDP-Sfer3", "unable to unmarshal event") } diff --git a/internal/repository/instance/custom_text.go b/internal/repository/instance/custom_text.go index e6fef42bb..992a88588 100644 --- a/internal/repository/instance/custom_text.go +++ b/internal/repository/instance/custom_text.go @@ -6,7 +6,6 @@ import ( "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -38,7 +37,7 @@ func NewCustomTextSetEvent( } } -func CustomTextSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextSetEventMapper(event) if err != nil { return nil, err @@ -67,7 +66,7 @@ func NewCustomTextRemovedEvent( } } -func CustomTextRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextRemovedEventMapper(event) if err != nil { return nil, err @@ -95,7 +94,7 @@ func NewCustomTextTemplateRemovedEvent( } } -func CustomTextTemplateRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextTemplateRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextTemplateRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/debug_notification_file.go b/internal/repository/instance/debug_notification_file.go index d886947a2..a8f410bda 100644 --- a/internal/repository/instance/debug_notification_file.go +++ b/internal/repository/instance/debug_notification_file.go @@ -5,8 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/settings" - - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -38,7 +36,7 @@ func NewDebugNotificationProviderFileAddedEvent( } } -func DebugNotificationProviderFileAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderFileAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderAddedEventMapper(event) if err != nil { return nil, err @@ -69,7 +67,7 @@ func NewDebugNotificationProviderFileChangedEvent( return &DebugNotificationProviderFileChangedEvent{DebugNotificationProviderChangedEvent: *changedEvent}, nil } -func DebugNotificationProviderFileChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderFileChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderChangedEventMapper(event) if err != nil { return nil, err @@ -96,7 +94,7 @@ func NewDebugNotificationProviderFileRemovedEvent( } } -func DebugNotificationProviderFileRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderFileRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/debug_notification_log.go b/internal/repository/instance/debug_notification_log.go index b9e090af0..6cd6754c1 100644 --- a/internal/repository/instance/debug_notification_log.go +++ b/internal/repository/instance/debug_notification_log.go @@ -5,8 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/settings" - - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -40,7 +38,7 @@ func NewDebugNotificationProviderLogAddedEvent( } } -func DebugNotificationProviderLogAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderLogAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderAddedEventMapper(event) if err != nil { return nil, err @@ -71,7 +69,7 @@ func NewDebugNotificationProviderLogChangedEvent( return &DebugNotificationProviderLogChangedEvent{DebugNotificationProviderChangedEvent: *changedEvent}, nil } -func DebugNotificationProviderLogChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderLogChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderChangedEventMapper(event) if err != nil { return nil, err @@ -98,7 +96,7 @@ func NewDebugNotificationProviderLogRemovedEvent( } } -func DebugNotificationProviderLogRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderLogRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := settings.DebugNotificationProviderRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/domain.go b/internal/repository/instance/domain.go index b1a4fa01b..0194bdbc2 100644 --- a/internal/repository/instance/domain.go +++ b/internal/repository/instance/domain.go @@ -2,12 +2,9 @@ package instance import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -18,15 +15,15 @@ const ( InstanceDomainRemovedEventType = domainEventPrefix + "removed" ) -func NewAddInstanceDomainUniqueConstraint(domain string) *eventstore.EventUniqueConstraint { - return eventstore.NewAddGlobalEventUniqueConstraint( +func NewAddInstanceDomainUniqueConstraint(domain string) *eventstore.UniqueConstraint { + return eventstore.NewAddGlobalUniqueConstraint( UniqueInstanceDomain, domain, "Errors.Instance.Domain.AlreadyExists") } -func NewRemoveInstanceDomainUniqueConstraint(domain string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveGlobalEventUniqueConstraint( +func NewRemoveInstanceDomainUniqueConstraint(domain string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveGlobalUniqueConstraint( UniqueInstanceDomain, domain) } @@ -38,12 +35,12 @@ type DomainAddedEvent struct { Generated bool `json:"generated,omitempty"` } -func (e *DomainAddedEvent) Data() interface{} { +func (e *DomainAddedEvent) Payload() interface{} { return e } -func (e *DomainAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddInstanceDomainUniqueConstraint(e.Domain)} +func (e *DomainAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddInstanceDomainUniqueConstraint(e.Domain)} } func NewDomainAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string, generated bool) *DomainAddedEvent { @@ -58,11 +55,11 @@ func NewDomainAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, d } } -func DomainAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { domainAdded := &DomainAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, domainAdded) + err := event.Unmarshal(domainAdded) if err != nil { return nil, errors.ThrowInternal(err, "INSTANCE-3noij", "unable to unmarshal instance domain added") } @@ -76,11 +73,11 @@ type DomainPrimarySetEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainPrimarySetEvent) Data() interface{} { +func (e *DomainPrimarySetEvent) Payload() interface{} { return e } -func (e *DomainPrimarySetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainPrimarySetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -95,11 +92,11 @@ func NewDomainPrimarySetEvent(ctx context.Context, aggregate *eventstore.Aggrega } } -func DomainPrimarySetEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPrimarySetEventMapper(event eventstore.Event) (eventstore.Event, error) { domainAdded := &DomainPrimarySetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, domainAdded) + err := event.Unmarshal(domainAdded) if err != nil { return nil, errors.ThrowInternal(err, "INSTANCE-29jöF", "unable to unmarshal instance domain added") } @@ -113,12 +110,12 @@ type DomainRemovedEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainRemovedEvent) Data() interface{} { +func (e *DomainRemovedEvent) Payload() interface{} { return e } -func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveInstanceDomainUniqueConstraint(e.Domain)} +func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveInstanceDomainUniqueConstraint(e.Domain)} } func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainRemovedEvent { @@ -132,11 +129,11 @@ func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, } } -func DomainRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { domainRemoved := &DomainRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, domainRemoved) + err := event.Unmarshal(domainRemoved) if err != nil { return nil, errors.ThrowInternal(err, "INSTANCE-BngB2", "unable to unmarshal instance domain removed") } diff --git a/internal/repository/instance/event_default_language.go b/internal/repository/instance/event_default_language.go index a596a0a5c..491266434 100644 --- a/internal/repository/instance/event_default_language.go +++ b/internal/repository/instance/event_default_language.go @@ -2,13 +2,11 @@ package instance import ( "context" - "encoding/json" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -21,11 +19,11 @@ type DefaultLanguageSetEvent struct { Language language.Tag `json:"language"` } -func (e *DefaultLanguageSetEvent) Data() interface{} { +func (e *DefaultLanguageSetEvent) Payload() interface{} { return e } -func (e *DefaultLanguageSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DefaultLanguageSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -44,11 +42,11 @@ func NewDefaultLanguageSetEvent( } } -func DefaultLanguageSetMapper(event *repository.Event) (eventstore.Event, error) { +func DefaultLanguageSetMapper(event eventstore.Event) (eventstore.Event, error) { e := &DefaultLanguageSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-3j9fs", "unable to unmarshal default language set") } diff --git a/internal/repository/instance/event_iam_project_set.go b/internal/repository/instance/event_iam_project_set.go index b1aac6985..b2f15fdd3 100644 --- a/internal/repository/instance/event_iam_project_set.go +++ b/internal/repository/instance/event_iam_project_set.go @@ -2,12 +2,9 @@ package instance import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -21,11 +18,11 @@ type ProjectSetEvent struct { ProjectID string `json:"iamProjectId"` } -func (e *ProjectSetEvent) Data() interface{} { +func (e *ProjectSetEvent) Payload() interface{} { return e } -func (e *ProjectSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ProjectSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -44,11 +41,11 @@ func NewIAMProjectSetEvent( } } -func ProjectSetMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectSetMapper(event eventstore.Event) (eventstore.Event, error) { e := &ProjectSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-cdFZH", "unable to unmarshal global org set") } @@ -63,11 +60,11 @@ type ConsoleSetEvent struct { AppID string `json:"appId"` } -func (e *ConsoleSetEvent) Data() interface{} { +func (e *ConsoleSetEvent) Payload() interface{} { return e } -func (e *ConsoleSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ConsoleSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -88,11 +85,11 @@ func NewIAMConsoleSetEvent( } } -func ConsoleSetMapper(event *repository.Event) (eventstore.Event, error) { +func ConsoleSetMapper(event eventstore.Event) (eventstore.Event, error) { e := &ConsoleSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-cdFZH", "unable to unmarshal console set") } diff --git a/internal/repository/instance/event_org_set.go b/internal/repository/instance/event_org_set.go index f960952c3..161e31713 100644 --- a/internal/repository/instance/event_org_set.go +++ b/internal/repository/instance/event_org_set.go @@ -2,12 +2,9 @@ package instance import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -20,11 +17,11 @@ type DefaultOrgSetEvent struct { OrgID string `json:"orgId"` } -func (e *DefaultOrgSetEvent) Data() interface{} { +func (e *DefaultOrgSetEvent) Payload() interface{} { return e } -func (e *DefaultOrgSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DefaultOrgSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -43,11 +40,11 @@ func NewDefaultOrgSetEventEvent( } } -func DefaultOrgSetMapper(event *repository.Event) (eventstore.Event, error) { +func DefaultOrgSetMapper(event eventstore.Event) (eventstore.Event, error) { e := &DefaultOrgSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-cdFZH", "unable to unmarshal default org set") } diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index 7e82c89b0..3bda1542d 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -6,7 +6,6 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/idp" ) @@ -80,7 +79,7 @@ func NewOAuthIDPAddedEvent( } } -func OAuthIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OAuthIDPAddedEventMapper(event) if err != nil { return nil, err @@ -115,7 +114,7 @@ func NewOAuthIDPChangedEvent( return &OAuthIDPChangedEvent{OAuthIDPChangedEvent: *changedEvent}, nil } -func OAuthIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OAuthIDPChangedEventMapper(event) if err != nil { return nil, err @@ -160,7 +159,7 @@ func NewOIDCIDPAddedEvent( } } -func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPAddedEventMapper(event) if err != nil { return nil, err @@ -195,7 +194,7 @@ func NewOIDCIDPChangedEvent( return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *changedEvent}, nil } -func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPChangedEventMapper(event) if err != nil { return nil, err @@ -239,7 +238,7 @@ func NewOIDCIDPMigratedAzureADEvent( } } -func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedAzureADEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPMigratedAzureADEventMapper(event) if err != nil { return nil, err @@ -279,7 +278,7 @@ func NewOIDCIDPMigratedGoogleEvent( } } -func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedGoogleEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPMigratedGoogleEventMapper(event) if err != nil { return nil, err @@ -322,7 +321,7 @@ func NewJWTIDPAddedEvent( } } -func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.JWTIDPAddedEventMapper(event) if err != nil { return nil, err @@ -357,7 +356,7 @@ func NewJWTIDPChangedEvent( return &JWTIDPChangedEvent{JWTIDPChangedEvent: *changedEvent}, nil } -func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.JWTIDPChangedEventMapper(event) if err != nil { return nil, err @@ -402,7 +401,7 @@ func NewAzureADIDPAddedEvent( } } -func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AzureADIDPAddedEventMapper(event) if err != nil { return nil, err @@ -437,7 +436,7 @@ func NewAzureADIDPChangedEvent( return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *changedEvent}, nil } -func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AzureADIDPChangedEventMapper(event) if err != nil { return nil, err @@ -478,7 +477,7 @@ func NewGitHubIDPAddedEvent( } } -func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubIDPAddedEventMapper(event) if err != nil { return nil, err @@ -513,7 +512,7 @@ func NewGitHubIDPChangedEvent( return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *changedEvent}, nil } -func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubIDPChangedEventMapper(event) if err != nil { return nil, err @@ -560,7 +559,7 @@ func NewGitHubEnterpriseIDPAddedEvent( } } -func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubEnterpriseIDPAddedEventMapper(event) if err != nil { return nil, err @@ -595,7 +594,7 @@ func NewGitHubEnterpriseIDPChangedEvent( return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *changedEvent}, nil } -func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubEnterpriseIDPChangedEventMapper(event) if err != nil { return nil, err @@ -636,7 +635,7 @@ func NewGitLabIDPAddedEvent( } } -func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabIDPAddedEventMapper(event) if err != nil { return nil, err @@ -671,7 +670,7 @@ func NewGitLabIDPChangedEvent( return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *changedEvent}, nil } -func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabIDPChangedEventMapper(event) if err != nil { return nil, err @@ -714,7 +713,7 @@ func NewGitLabSelfHostedIDPAddedEvent( } } -func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabSelfHostedIDPAddedEventMapper(event) if err != nil { return nil, err @@ -749,7 +748,7 @@ func NewGitLabSelfHostedIDPChangedEvent( return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *changedEvent}, nil } -func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabSelfHostedIDPChangedEventMapper(event) if err != nil { return nil, err @@ -790,7 +789,7 @@ func NewGoogleIDPAddedEvent( } } -func GoogleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GoogleIDPAddedEventMapper(event) if err != nil { return nil, err @@ -825,7 +824,7 @@ func NewGoogleIDPChangedEvent( return &GoogleIDPChangedEvent{GoogleIDPChangedEvent: *changedEvent}, nil } -func GoogleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GoogleIDPChangedEventMapper(event) if err != nil { return nil, err @@ -880,7 +879,7 @@ func NewLDAPIDPAddedEvent( } } -func LDAPIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.LDAPIDPAddedEventMapper(event) if err != nil { return nil, err @@ -914,7 +913,7 @@ func NewLDAPIDPChangedEvent( return &LDAPIDPChangedEvent{LDAPIDPChangedEvent: *changedEvent}, nil } -func LDAPIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.LDAPIDPChangedEventMapper(event) if err != nil { return nil, err @@ -959,7 +958,7 @@ func NewAppleIDPAddedEvent( } } -func AppleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AppleIDPAddedEventMapper(event) if err != nil { return nil, err @@ -994,7 +993,7 @@ func NewAppleIDPChangedEvent( return &AppleIDPChangedEvent{AppleIDPChangedEvent: *changedEvent}, nil } -func AppleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AppleIDPChangedEventMapper(event) if err != nil { return nil, err @@ -1038,7 +1037,7 @@ func NewSAMLIDPAddedEvent( } } -func SAMLIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.SAMLIDPAddedEventMapper(event) if err != nil { return nil, err @@ -1073,7 +1072,7 @@ func NewSAMLIDPChangedEvent( return &SAMLIDPChangedEvent{SAMLIDPChangedEvent: *changedEvent}, nil } -func SAMLIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.SAMLIDPChangedEventMapper(event) if err != nil { return nil, err @@ -1103,11 +1102,11 @@ func NewIDPRemovedEvent( } } -func (e *IDPRemovedEvent) Data() interface{} { +func (e *IDPRemovedEvent) Payload() interface{} { return e } -func IDPRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.RemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/idp_config.go b/internal/repository/instance/idp_config.go index ce3aad857..da3d432ac 100644 --- a/internal/repository/instance/idp_config.go +++ b/internal/repository/instance/idp_config.go @@ -3,10 +3,8 @@ package instance import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -48,7 +46,7 @@ func NewIDPConfigAddedEvent( } } -func IDPConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigAddedEventMapper(event) if err != nil { return nil, err @@ -83,7 +81,7 @@ func NewIDPConfigChangedEvent( return &IDPConfigChangedEvent{IDPConfigChangedEvent: *changeEvent}, nil } -func IDPConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigChangedEventMapper(event) if err != nil { return nil, err @@ -115,7 +113,7 @@ func NewIDPConfigRemovedEvent( } } -func IDPConfigRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigRemovedEventMapper(event) if err != nil { return nil, err @@ -146,7 +144,7 @@ func NewIDPConfigDeactivatedEvent( } } -func IDPConfigDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigDeactivatedEventMapper(event) if err != nil { return nil, err @@ -177,7 +175,7 @@ func NewIDPConfigReactivatedEvent( } } -func IDPConfigReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigReactivatedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/idp_jwt_config.go b/internal/repository/instance/idp_jwt_config.go index bc21f78d0..912783b45 100644 --- a/internal/repository/instance/idp_jwt_config.go +++ b/internal/repository/instance/idp_jwt_config.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -43,7 +41,7 @@ func NewIDPJWTConfigAddedEvent( } } -func IDPJWTConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPJWTConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.JWTConfigAddedEventMapper(event) if err != nil { return nil, err @@ -76,7 +74,7 @@ func NewIDPJWTConfigChangedEvent( return &IDPJWTConfigChangedEvent{JWTConfigChangedEvent: *changeEvent}, nil } -func IDPJWTConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPJWTConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.JWTConfigChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/idp_oidc_config.go b/internal/repository/instance/idp_oidc_config.go index 0b1bd0567..b702eb2f8 100644 --- a/internal/repository/instance/idp_oidc_config.go +++ b/internal/repository/instance/idp_oidc_config.go @@ -3,11 +3,9 @@ package instance import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -54,7 +52,7 @@ func NewIDPOIDCConfigAddedEvent( } } -func IDPOIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPOIDCConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.OIDCConfigAddedEventMapper(event) if err != nil { return nil, err @@ -87,7 +85,7 @@ func NewIDPOIDCConfigChangedEvent( return &IDPOIDCConfigChangedEvent{OIDCConfigChangedEvent: *changeEvent}, nil } -func IDPOIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPOIDCConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.OIDCConfigChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/instance.go b/internal/repository/instance/instance.go index cb5dd220a..8480fd17c 100644 --- a/internal/repository/instance/instance.go +++ b/internal/repository/instance/instance.go @@ -2,11 +2,9 @@ package instance import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -21,11 +19,11 @@ type InstanceAddedEvent struct { Name string `json:"name,omitempty"` } -func (e *InstanceAddedEvent) Data() interface{} { +func (e *InstanceAddedEvent) Payload() interface{} { return e } -func (e *InstanceAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *InstanceAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -40,11 +38,11 @@ func NewInstanceAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, } } -func InstanceAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func InstanceAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { instanceAdded := &InstanceAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, instanceAdded) + err := event.Unmarshal(instanceAdded) if err != nil { return nil, errors.ThrowInternal(err, "INSTANCE-s9l3F", "unable to unmarshal instance added") } @@ -58,11 +56,11 @@ type InstanceChangedEvent struct { Name string `json:"name,omitempty"` } -func (e *InstanceChangedEvent) Data() interface{} { +func (e *InstanceChangedEvent) Payload() interface{} { return e } -func (e *InstanceChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *InstanceChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -77,11 +75,11 @@ func NewInstanceChangedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func InstanceChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func InstanceChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { instanceChanged := &InstanceChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, instanceChanged) + err := event.Unmarshal(instanceChanged) if err != nil { return nil, errors.ThrowInternal(err, "INSTANCE-3hfo8", "unable to unmarshal instance changed") } @@ -95,12 +93,12 @@ type InstanceRemovedEvent struct { domains []string } -func (e *InstanceRemovedEvent) Data() interface{} { +func (e *InstanceRemovedEvent) Payload() interface{} { return nil } -func (e *InstanceRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - constraints := make([]*eventstore.EventUniqueConstraint, len(e.domains)+1) +func (e *InstanceRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + constraints := make([]*eventstore.UniqueConstraint, len(e.domains)+1) for i, domain := range e.domains { constraints[i] = NewRemoveInstanceDomainUniqueConstraint(domain) } @@ -120,7 +118,7 @@ func NewInstanceRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func InstanceRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func InstanceRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &InstanceRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/instance/member.go b/internal/repository/instance/member.go index baf6b7fde..0518aab47 100644 --- a/internal/repository/instance/member.go +++ b/internal/repository/instance/member.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/member" ) @@ -40,7 +38,7 @@ func NewMemberAddedEvent( } } -func MemberAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.MemberAddedEventMapper(event) if err != nil { return nil, err @@ -72,7 +70,7 @@ func NewMemberChangedEvent( } } -func MemberChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.ChangedEventMapper(event) if err != nil { return nil, err @@ -102,7 +100,7 @@ func NewMemberRemovedEvent( } } -func MemberRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.RemovedEventMapper(event) if err != nil { return nil, err @@ -132,7 +130,7 @@ func NewMemberCascadeRemovedEvent( } } -func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.CascadeRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/oidc_settings.go b/internal/repository/instance/oidc_settings.go index b33f2265e..edef8d2c6 100644 --- a/internal/repository/instance/oidc_settings.go +++ b/internal/repository/instance/oidc_settings.go @@ -2,12 +2,10 @@ package instance import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -46,19 +44,19 @@ func NewOIDCSettingsAddedEvent( } } -func (e *OIDCSettingsAddedEvent) Data() interface{} { +func (e *OIDCSettingsAddedEvent) Payload() interface{} { return e } -func (e *OIDCSettingsAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCSettingsAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func OIDCSettingsAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCSettingsAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { oidcSettingsAdded := &OIDCSettingsAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, oidcSettingsAdded) + err := event.Unmarshal(oidcSettingsAdded) if err != nil { return nil, errors.ThrowInternal(err, "IAM-soiwj", "unable to unmarshal oidc config added") } @@ -75,11 +73,11 @@ type OIDCSettingsChangedEvent struct { RefreshTokenExpiration *time.Duration `json:"refreshTokenExpiration,omitempty"` } -func (e *OIDCSettingsChangedEvent) Data() interface{} { +func (e *OIDCSettingsChangedEvent) Payload() interface{} { return e } -func (e *OIDCSettingsChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCSettingsChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -130,12 +128,12 @@ func ChangeOIDCSettingsRefreshTokenExpiration(refreshTokenExpiration time.Durati } } -func OIDCSettingsChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCSettingsChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCSettingsChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-f98uf", "unable to unmarshal oidc settings changed") } diff --git a/internal/repository/instance/policy_domain.go b/internal/repository/instance/policy_domain.go index b4d6bb36c..fbd307e68 100644 --- a/internal/repository/instance/policy_domain.go +++ b/internal/repository/instance/policy_domain.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -38,7 +36,7 @@ func NewDomainPolicyAddedEvent( } } -func DomainPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.DomainPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -69,7 +67,7 @@ func NewDomainPolicyChangedEvent( return &DomainPolicyChangedEvent{DomainPolicyChangedEvent: *changedEvent}, nil } -func DomainPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.DomainPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_label.go b/internal/repository/instance/policy_label.go index 97949935d..6b14b1cef 100644 --- a/internal/repository/instance/policy_label.go +++ b/internal/repository/instance/policy_label.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -68,7 +66,7 @@ func NewLabelPolicyAddedEvent( } } -func LabelPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -99,7 +97,7 @@ func NewLabelPolicyChangedEvent( return &LabelPolicyChangedEvent{LabelPolicyChangedEvent: *changedEvent}, nil } -func LabelPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -126,7 +124,7 @@ func NewLabelPolicyActivatedEvent( } } -func LabelPolicyActivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyActivatedEventMapper(event) if err != nil { return nil, err @@ -155,7 +153,7 @@ func NewLabelPolicyLogoAddedEvent( } } -func LabelPolicyLogoAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoAddedEventMapper(event) if err != nil { return nil, err @@ -184,7 +182,7 @@ func NewLabelPolicyLogoRemovedEvent( } } -func LabelPolicyLogoRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoRemovedEventMapper(event) if err != nil { return nil, err @@ -213,7 +211,7 @@ func NewLabelPolicyIconAddedEvent( } } -func LabelPolicyIconAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconAddedEventMapper(event) if err != nil { return nil, err @@ -242,7 +240,7 @@ func NewLabelPolicyIconRemovedEvent( } } -func LabelPolicyIconRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconRemovedEventMapper(event) if err != nil { return nil, err @@ -271,7 +269,7 @@ func NewLabelPolicyLogoDarkAddedEvent( } } -func LabelPolicyLogoDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoDarkAddedEventMapper(event) if err != nil { return nil, err @@ -300,7 +298,7 @@ func NewLabelPolicyLogoDarkRemovedEvent( } } -func LabelPolicyLogoDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoDarkRemovedEventMapper(event) if err != nil { return nil, err @@ -329,7 +327,7 @@ func NewLabelPolicyIconDarkAddedEvent( } } -func LabelPolicyIconDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconDarkAddedEventMapper(event) if err != nil { return nil, err @@ -358,7 +356,7 @@ func NewLabelPolicyIconDarkRemovedEvent( } } -func LabelPolicyIconDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconDarkRemovedEventMapper(event) if err != nil { return nil, err @@ -387,7 +385,7 @@ func NewLabelPolicyFontAddedEvent( } } -func LabelPolicyFontAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyFontAddedEventMapper(event) if err != nil { return nil, err @@ -416,7 +414,7 @@ func NewLabelPolicyFontRemovedEvent( } } -func LabelPolicyFontRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyFontRemovedEventMapper(event) if err != nil { return nil, err @@ -429,11 +427,11 @@ type LabelPolicyAssetsRemovedEvent struct { policy.LabelPolicyAssetsRemovedEvent } -func (e *LabelPolicyAssetsRemovedEvent) Data() interface{} { +func (e *LabelPolicyAssetsRemovedEvent) Payload() interface{} { return nil } -func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -451,7 +449,7 @@ func NewLabelPolicyAssetsRemovedEvent( } } -func LabelPolicyAssetsRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAssetsRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyAssetsRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_login.go b/internal/repository/instance/policy_login.go index 73a0b3ef4..8085f6d31 100644 --- a/internal/repository/instance/policy_login.go +++ b/internal/repository/instance/policy_login.go @@ -4,10 +4,8 @@ import ( "context" "time" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -67,7 +65,7 @@ func NewLoginPolicyAddedEvent( } } -func LoginPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LoginPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -98,7 +96,7 @@ func NewLoginPolicyChangedEvent( return &LoginPolicyChangedEvent{LoginPolicyChangedEvent: *changedEvent}, nil } -func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LoginPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_login_factors.go b/internal/repository/instance/policy_login_factors.go index 2697ac5d8..466e28637 100644 --- a/internal/repository/instance/policy_login_factors.go +++ b/internal/repository/instance/policy_login_factors.go @@ -3,10 +3,8 @@ package instance import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -37,7 +35,7 @@ func NewLoginPolicySecondFactorAddedEvent( } } -func SecondFactorAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.SecondFactorAddedEventMapper(event) if err != nil { return nil, err @@ -68,7 +66,7 @@ func NewLoginPolicySecondFactorRemovedEvent( } } -func SecondFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.SecondFactorRemovedEventMapper(event) if err != nil { return nil, err @@ -98,7 +96,7 @@ func NewLoginPolicyMultiFactorAddedEvent( } } -func MultiFactorAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MultiFactorAddedEventMapper(event) if err != nil { return nil, err @@ -129,7 +127,7 @@ func NewLoginPolicyMultiFactorRemovedEvent( } } -func MultiFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MultiFactorRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_login_identity_provider.go b/internal/repository/instance/policy_login_identity_provider.go index b7471e4c5..16504b6a0 100644 --- a/internal/repository/instance/policy_login_identity_provider.go +++ b/internal/repository/instance/policy_login_identity_provider.go @@ -3,10 +3,8 @@ package instance import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -37,7 +35,7 @@ func NewIdentityProviderAddedEvent( } } -func IdentityProviderAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderAddedEventMapper(event) if err != nil { return nil, err @@ -67,7 +65,7 @@ func NewIdentityProviderRemovedEvent( } } -func IdentityProviderRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderRemovedEventMapper(event) if err != nil { return nil, err @@ -94,7 +92,7 @@ func NewIdentityProviderCascadeRemovedEvent( } } -func IdentityProviderCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderCascadeRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_mail_template.go b/internal/repository/instance/policy_mail_template.go index b159066be..7db645298 100644 --- a/internal/repository/instance/policy_mail_template.go +++ b/internal/repository/instance/policy_mail_template.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -29,7 +28,7 @@ func NewMailTemplateAddedEvent( } } -func MailTemplateAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTemplateAddedEventMapper(event) if err != nil { return nil, err @@ -57,7 +56,7 @@ func NewMailTemplateChangedEvent( return &MailTemplateChangedEvent{MailTemplateChangedEvent: *changedEvent}, nil } -func MailTemplateChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTemplateChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_mail_text.go b/internal/repository/instance/policy_mail_text.go index 5feb94311..912c956b8 100644 --- a/internal/repository/instance/policy_mail_text.go +++ b/internal/repository/instance/policy_mail_text.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -43,7 +42,7 @@ func NewMailTextAddedEvent( } } -func MailTextAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTextAddedEventMapper(event) if err != nil { return nil, err @@ -75,7 +74,7 @@ func NewMailTextChangedEvent( return &MailTextChangedEvent{MailTextChangedEvent: *changedEvent}, nil } -func MailTextChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTextChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_notification.go b/internal/repository/instance/policy_notification.go index ae8eb7a30..243433362 100644 --- a/internal/repository/instance/policy_notification.go +++ b/internal/repository/instance/policy_notification.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -32,7 +31,7 @@ func NewNotificationPolicyAddedEvent( } } -func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.NotificationPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -63,7 +62,7 @@ func NewNotificationPolicyChangedEvent( return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *changedEvent}, nil } -func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.NotificationPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_password_age.go b/internal/repository/instance/policy_password_age.go index a805af4f7..11d2b4288 100644 --- a/internal/repository/instance/policy_password_age.go +++ b/internal/repository/instance/policy_password_age.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -35,7 +33,7 @@ func NewPasswordAgePolicyAddedEvent( } } -func PasswordAgePolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordAgePolicyAddedEventMapper(event) if err != nil { return nil, err @@ -66,7 +64,7 @@ func NewPasswordAgePolicyChangedEvent( return &PasswordAgePolicyChangedEvent{PasswordAgePolicyChangedEvent: *changedEvent}, nil } -func PasswordAgePolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordAgePolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_password_complexity.go b/internal/repository/instance/policy_password_complexity.go index 5cac8c842..8c46d55f2 100644 --- a/internal/repository/instance/policy_password_complexity.go +++ b/internal/repository/instance/policy_password_complexity.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -41,7 +39,7 @@ func NewPasswordComplexityPolicyAddedEvent( } } -func PasswordComplexityPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordComplexityPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -72,7 +70,7 @@ func NewPasswordComplexityPolicyChangedEvent( return &PasswordComplexityPolicyChangedEvent{PasswordComplexityPolicyChangedEvent: *changedEvent}, nil } -func PasswordComplexityPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordComplexityPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_password_lockout.go b/internal/repository/instance/policy_password_lockout.go index ab6a34084..671b53961 100644 --- a/internal/repository/instance/policy_password_lockout.go +++ b/internal/repository/instance/policy_password_lockout.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -35,7 +33,7 @@ func NewLockoutPolicyAddedEvent( } } -func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LockoutPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -66,7 +64,7 @@ func NewLockoutPolicyChangedEvent( return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *changedEvent}, nil } -func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LockoutPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_privacy.go b/internal/repository/instance/policy_privacy.go index 2851ba784..ec374583b 100644 --- a/internal/repository/instance/policy_privacy.go +++ b/internal/repository/instance/policy_privacy.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -39,7 +38,7 @@ func NewPrivacyPolicyAddedEvent( } } -func PrivacyPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PrivacyPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -70,7 +69,7 @@ func NewPrivacyPolicyChangedEvent( return &PrivacyPolicyChangedEvent{PrivacyPolicyChangedEvent: *changedEvent}, nil } -func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PrivacyPolicyChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/instance/policy_security.go b/internal/repository/instance/policy_security.go index 255294035..4faeb8ecc 100644 --- a/internal/repository/instance/policy_security.go +++ b/internal/repository/instance/policy_security.go @@ -2,11 +2,9 @@ package instance import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -59,19 +57,19 @@ func ChangeSecurityPolicyAllowedOrigins(allowedOrigins []string) func(event *Sec } } -func (e *SecurityPolicySetEvent) Data() interface{} { +func (e *SecurityPolicySetEvent) Payload() interface{} { return e } -func (e *SecurityPolicySetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SecurityPolicySetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SecurityPolicySetEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecurityPolicySetEventMapper(event eventstore.Event) (eventstore.Event, error) { securityPolicyAdded := &SecurityPolicySetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, securityPolicyAdded) + err := event.Unmarshal(securityPolicyAdded) if err != nil { return nil, errors.ThrowInternal(err, "IAM-soiwj", "unable to unmarshal oidc config added") } diff --git a/internal/repository/instance/secret_generator.go b/internal/repository/instance/secret_generator.go index d2f25422d..4af5387c0 100644 --- a/internal/repository/instance/secret_generator.go +++ b/internal/repository/instance/secret_generator.go @@ -2,13 +2,11 @@ package instance import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -19,15 +17,15 @@ const ( SecretGeneratorRemovedEventType = instanceEventTypePrefix + secretGeneratorPrefix + "removed" ) -func NewAddSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.EventUniqueConstraint { +func NewAddSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueSecretGeneratorType, string(generatorType), "Errors.SecretGenerator.AlreadyExists") } -func NewRemoveSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueSecretGeneratorType, string(generatorType)) } @@ -71,19 +69,19 @@ func NewSecretGeneratorAddedEvent( } } -func (e *SecretGeneratorAddedEvent) Data() interface{} { +func (e *SecretGeneratorAddedEvent) Payload() interface{} { return e } -func (e *SecretGeneratorAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddSecretGeneratorTypeUniqueConstraint(e.GeneratorType)} +func (e *SecretGeneratorAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddSecretGeneratorTypeUniqueConstraint(e.GeneratorType)} } -func SecretGeneratorAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecretGeneratorAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { secretGeneratorAdded := &SecretGeneratorAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, secretGeneratorAdded) + err := event.Unmarshal(secretGeneratorAdded) if err != nil { return nil, errors.ThrowInternal(err, "IAM-en9f4", "unable to unmarshal secret generator added") } @@ -103,11 +101,11 @@ type SecretGeneratorChangedEvent struct { IncludeSymbols *bool `json:"includeSymbols,omitempty"` } -func (e *SecretGeneratorChangedEvent) Data() interface{} { +func (e *SecretGeneratorChangedEvent) Payload() interface{} { return e } -func (e *SecretGeneratorChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SecretGeneratorChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -172,12 +170,12 @@ func ChangeSecretGeneratorIncludeSymbols(includeSymbols bool) func(event *Secret } } -func SecretGeneratorChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecretGeneratorChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SecretGeneratorChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-2m09e", "unable to unmarshal secret generator changed") } @@ -191,12 +189,12 @@ type SecretGeneratorRemovedEvent struct { GeneratorType domain.SecretGeneratorType `json:"generatorType"` } -func (e *SecretGeneratorRemovedEvent) Data() interface{} { +func (e *SecretGeneratorRemovedEvent) Payload() interface{} { return e } -func (e *SecretGeneratorRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveSecretGeneratorTypeUniqueConstraint(e.GeneratorType)} +func (e *SecretGeneratorRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveSecretGeneratorTypeUniqueConstraint(e.GeneratorType)} } func NewSecretGeneratorRemovedEvent( @@ -214,12 +212,12 @@ func NewSecretGeneratorRemovedEvent( } } -func SecretGeneratorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecretGeneratorRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SecretGeneratorRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-m09ke", "unable to unmarshal secret generator removed") } diff --git a/internal/repository/instance/sms.go b/internal/repository/instance/sms.go index c406ba1f7..dfa7e83c2 100644 --- a/internal/repository/instance/sms.go +++ b/internal/repository/instance/sms.go @@ -2,12 +2,10 @@ package instance import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -51,19 +49,19 @@ func NewSMSConfigTwilioAddedEvent( } } -func (e *SMSConfigTwilioAddedEvent) Data() interface{} { +func (e *SMSConfigTwilioAddedEvent) Payload() interface{} { return e } -func (e *SMSConfigTwilioAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigTwilioAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigTwilioAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigTwilioAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { smsConfigAdded := &SMSConfigTwilioAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smsConfigAdded) + err := event.Unmarshal(smsConfigAdded) if err != nil { return nil, errors.ThrowInternal(err, "IAM-smwiR", "unable to unmarshal sms config twilio added") } @@ -116,19 +114,19 @@ func ChangeSMSConfigTwilioSenderNumber(senderNumber string) func(event *SMSConfi } } -func (e *SMSConfigTwilioChangedEvent) Data() interface{} { +func (e *SMSConfigTwilioChangedEvent) Payload() interface{} { return e } -func (e *SMSConfigTwilioChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigTwilioChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigTwilioChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigTwilioChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { smsConfigChanged := &SMSConfigTwilioChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smsConfigChanged) + err := event.Unmarshal(smsConfigChanged) if err != nil { return nil, errors.ThrowInternal(err, "IAM-smwiR", "unable to unmarshal sms config twilio added") } @@ -160,19 +158,19 @@ func NewSMSConfigTokenChangedEvent( } } -func (e *SMSConfigTwilioTokenChangedEvent) Data() interface{} { +func (e *SMSConfigTwilioTokenChangedEvent) Payload() interface{} { return e } -func (e *SMSConfigTwilioTokenChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigTwilioTokenChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigTwilioTokenChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigTwilioTokenChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { smtpConfigTokenChagned := &SMSConfigTwilioTokenChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smtpConfigTokenChagned) + err := event.Unmarshal(smtpConfigTokenChagned) if err != nil { return nil, errors.ThrowInternal(err, "IAM-fi9Wf", "unable to unmarshal sms config token changed") } @@ -200,19 +198,19 @@ func NewSMSConfigTwilioActivatedEvent( } } -func (e *SMSConfigActivatedEvent) Data() interface{} { +func (e *SMSConfigActivatedEvent) Payload() interface{} { return e } -func (e *SMSConfigActivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigActivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigActivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { smsConfigActivated := &SMSConfigActivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smsConfigActivated) + err := event.Unmarshal(smsConfigActivated) if err != nil { return nil, errors.ThrowInternal(err, "IAM-dn92f", "unable to unmarshal sms config twilio activated changed") } @@ -240,19 +238,19 @@ func NewSMSConfigDeactivatedEvent( } } -func (e *SMSConfigDeactivatedEvent) Data() interface{} { +func (e *SMSConfigDeactivatedEvent) Payload() interface{} { return e } -func (e *SMSConfigDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { smsConfigDeactivated := &SMSConfigDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smsConfigDeactivated) + err := event.Unmarshal(smsConfigDeactivated) if err != nil { return nil, errors.ThrowInternal(err, "IAM-dn92f", "unable to unmarshal sms config twilio deactivated changed") } @@ -280,19 +278,19 @@ func NewSMSConfigRemovedEvent( } } -func (e *SMSConfigRemovedEvent) Data() interface{} { +func (e *SMSConfigRemovedEvent) Payload() interface{} { return e } -func (e *SMSConfigRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMSConfigRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMSConfigRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMSConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { smsConfigRemoved := &SMSConfigRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smsConfigRemoved) + err := event.Unmarshal(smsConfigRemoved) if err != nil { return nil, errors.ThrowInternal(err, "IAM-99iNF", "unable to unmarshal sms config removed") } diff --git a/internal/repository/instance/smtp_config.go b/internal/repository/instance/smtp_config.go index a8831e489..eccb18dfe 100644 --- a/internal/repository/instance/smtp_config.go +++ b/internal/repository/instance/smtp_config.go @@ -2,12 +2,10 @@ package instance import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -57,19 +55,19 @@ func NewSMTPConfigAddedEvent( } } -func (e *SMTPConfigAddedEvent) Data() interface{} { +func (e *SMTPConfigAddedEvent) Payload() interface{} { return e } -func (e *SMTPConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMTPConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMTPConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMTPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { smtpConfigAdded := &SMTPConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smtpConfigAdded) + err := event.Unmarshal(smtpConfigAdded) if err != nil { return nil, errors.ThrowInternal(err, "IAM-39fks", "unable to unmarshal smtp config added") } @@ -88,11 +86,11 @@ type SMTPConfigChangedEvent struct { User *string `json:"user,omitempty"` } -func (e *SMTPConfigChangedEvent) Data() interface{} { +func (e *SMTPConfigChangedEvent) Payload() interface{} { return e } -func (e *SMTPConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMTPConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -155,12 +153,12 @@ func ChangeSMTPConfigSMTPUser(smtpUser string) func(event *SMTPConfigChangedEven } } -func SMTPConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMTPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SMTPConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "IAM-m09oo", "unable to unmarshal smtp changed") } @@ -189,19 +187,19 @@ func NewSMTPConfigPasswordChangedEvent( } } -func (e *SMTPConfigPasswordChangedEvent) Data() interface{} { +func (e *SMTPConfigPasswordChangedEvent) Payload() interface{} { return e } -func (e *SMTPConfigPasswordChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMTPConfigPasswordChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMTPConfigPasswordChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMTPConfigPasswordChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { smtpConfigPasswordChagned := &SMTPConfigPasswordChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smtpConfigPasswordChagned) + err := event.Unmarshal(smtpConfigPasswordChagned) if err != nil { return nil, errors.ThrowInternal(err, "IAM-99iNF", "unable to unmarshal smtp config password changed") } @@ -226,19 +224,19 @@ func NewSMTPConfigRemovedEvent( } } -func (e *SMTPConfigRemovedEvent) Data() interface{} { +func (e *SMTPConfigRemovedEvent) Payload() interface{} { return e } -func (e *SMTPConfigRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SMTPConfigRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } -func SMTPConfigRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SMTPConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { smtpConfigRemoved := &SMTPConfigRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, smtpConfigRemoved) + err := event.Unmarshal(smtpConfigRemoved) if err != nil { return nil, errors.ThrowInternal(err, "IAM-DVw1s", "unable to unmarshal smtp config removed") } diff --git a/internal/repository/keypair/certificate.go b/internal/repository/keypair/certificate.go index de3ffabb7..9a49ccc7d 100644 --- a/internal/repository/keypair/certificate.go +++ b/internal/repository/keypair/certificate.go @@ -2,13 +2,11 @@ package keypair import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -21,11 +19,11 @@ type AddedCertificateEvent struct { Certificate *Key `json:"certificate"` } -func (e *AddedCertificateEvent) Data() interface{} { +func (e *AddedCertificateEvent) Payload() interface{} { return e } -func (e *AddedCertificateEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedCertificateEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -47,12 +45,12 @@ func NewAddedCertificateEvent( } } -func AddedCertificateEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedCertificateEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AddedCertificateEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "KEY-4n9vs", "unable to unmarshal certificate added") } diff --git a/internal/repository/keypair/key_pair.go b/internal/repository/keypair/key_pair.go index 35b58e5d7..99b57555d 100644 --- a/internal/repository/keypair/key_pair.go +++ b/internal/repository/keypair/key_pair.go @@ -2,14 +2,12 @@ package keypair import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -31,11 +29,11 @@ type Key struct { Expiry time.Time `json:"expiry"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -67,12 +65,12 @@ func NewAddedEvent( } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "KEY-4n8vs", "unable to unmarshal key pair added") } diff --git a/internal/repository/member/events.go b/internal/repository/member/events.go index e19f99f6a..3ead301f0 100644 --- a/internal/repository/member/events.go +++ b/internal/repository/member/events.go @@ -1,12 +1,10 @@ package member import ( - "encoding/json" "fmt" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -17,15 +15,15 @@ const ( CascadeRemovedEventType = "member.cascade.removed" ) -func NewAddMemberUniqueConstraint(aggregateID, userID string) *eventstore.EventUniqueConstraint { +func NewAddMemberUniqueConstraint(aggregateID, userID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueMember, fmt.Sprintf("%s:%s", aggregateID, userID), "Errors.Member.AlreadyExists") } -func NewRemoveMemberUniqueConstraint(aggregateID, userID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveMemberUniqueConstraint(aggregateID, userID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueMember, fmt.Sprintf("%s:%s", aggregateID, userID), ) @@ -38,12 +36,12 @@ type MemberAddedEvent struct { UserID string `json:"userId"` } -func (e *MemberAddedEvent) Data() interface{} { +func (e *MemberAddedEvent) Payload() interface{} { return e } -func (e *MemberAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} +func (e *MemberAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} } func NewMemberAddedEvent( @@ -59,12 +57,12 @@ func NewMemberAddedEvent( } } -func MemberAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MemberAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-puqv4", "unable to unmarshal label policy") } @@ -79,11 +77,11 @@ type MemberChangedEvent struct { UserID string `json:"userId,omitempty"` } -func (e *MemberChangedEvent) Data() interface{} { +func (e *MemberChangedEvent) Payload() interface{} { return e } -func (e *MemberChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MemberChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -99,12 +97,12 @@ func NewMemberChangedEvent( } } -func ChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MemberChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-puqv4", "unable to unmarshal label policy") } @@ -118,12 +116,12 @@ type MemberRemovedEvent struct { UserID string `json:"userId"` } -func (e *MemberRemovedEvent) Data() interface{} { +func (e *MemberRemovedEvent) Payload() interface{} { return e } -func (e *MemberRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} +func (e *MemberRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} } func NewRemovedEvent( @@ -137,12 +135,12 @@ func NewRemovedEvent( } } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MemberRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "MEMBER-Ep4ip", "unable to unmarshal label policy") } @@ -156,12 +154,12 @@ type MemberCascadeRemovedEvent struct { UserID string `json:"userId"` } -func (e *MemberCascadeRemovedEvent) Data() interface{} { +func (e *MemberCascadeRemovedEvent) Payload() interface{} { return e } -func (e *MemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} +func (e *MemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} } func NewCascadeRemovedEvent( @@ -175,12 +173,12 @@ func NewCascadeRemovedEvent( } } -func CascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MemberCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "MEMBER-3j9sf", "unable to unmarshal label policy") } diff --git a/internal/repository/metadata/metadata.go b/internal/repository/metadata/metadata.go index 20b150c4c..7026badb1 100644 --- a/internal/repository/metadata/metadata.go +++ b/internal/repository/metadata/metadata.go @@ -1,11 +1,8 @@ package metadata import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -21,11 +18,11 @@ type SetEvent struct { Value []byte `json:"value"` } -func (e *SetEvent) Data() interface{} { +func (e *SetEvent) Payload() interface{} { return e } -func (e *SetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -41,12 +38,12 @@ func NewSetEvent( } } -func SetEventMapper(event *repository.Event) (eventstore.Event, error) { +func SetEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "META-3n9fs", "unable to unmarshal metadata set") } @@ -60,11 +57,11 @@ type RemovedEvent struct { Key string `json:"key"` } -func (e *RemovedEvent) Data() interface{} { +func (e *RemovedEvent) Payload() interface{} { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -79,12 +76,12 @@ func NewRemovedEvent( } } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "META-2m99f", "unable to unmarshal metadata removed") } @@ -96,11 +93,11 @@ type RemovedAllEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *RemovedAllEvent) Data() interface{} { +func (e *RemovedAllEvent) Payload() interface{} { return nil } -func (e *RemovedAllEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RemovedAllEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -113,7 +110,7 @@ func NewRemovedAllEvent( } } -func RemovedAllEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedAllEventMapper(event eventstore.Event) (eventstore.Event, error) { return &RemovedAllEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/milestone/events.go b/internal/repository/milestone/events.go index 5ec2f96f9..efd25949b 100644 --- a/internal/repository/milestone/events.go +++ b/internal/repository/milestone/events.go @@ -11,6 +11,8 @@ const ( PushedEventType = eventTypePrefix + "pushed" ) +var _ eventstore.Command = (*PushedEvent)(nil) + type PushedEvent struct { *eventstore.BaseEvent `json:"-"` MilestoneType Type `json:"type"` @@ -19,11 +21,12 @@ type PushedEvent struct { Endpoints []string `json:"endpoints"` } -func (p *PushedEvent) Data() interface{} { +// Payload implements eventstore.Command. +func (p *PushedEvent) Payload() any { return p } -func (p *PushedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (p *PushedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/oidcsession/oidc_session.go b/internal/repository/oidcsession/oidc_session.go index 8887b6192..4993a972e 100644 --- a/internal/repository/oidcsession/oidc_session.go +++ b/internal/repository/oidcsession/oidc_session.go @@ -30,11 +30,11 @@ type AddedEvent struct { AuthTime time.Time `json:"authTime"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -76,11 +76,11 @@ type AccessTokenAddedEvent struct { Lifetime time.Duration `json:"lifetime"` } -func (e *AccessTokenAddedEvent) Data() interface{} { +func (e *AccessTokenAddedEvent) Payload() interface{} { return e } -func (e *AccessTokenAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AccessTokenAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -111,11 +111,11 @@ type AccessTokenRevokedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *AccessTokenRevokedEvent) Data() interface{} { +func (e *AccessTokenRevokedEvent) Payload() interface{} { return e } -func (e *AccessTokenRevokedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AccessTokenRevokedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -144,11 +144,11 @@ type RefreshTokenAddedEvent struct { IdleLifetime time.Duration `json:"idleLifetime"` } -func (e *RefreshTokenAddedEvent) Data() interface{} { +func (e *RefreshTokenAddedEvent) Payload() interface{} { return e } -func (e *RefreshTokenAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RefreshTokenAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -182,11 +182,11 @@ type RefreshTokenRenewedEvent struct { IdleLifetime time.Duration `json:"idleLifetime"` } -func (e *RefreshTokenRenewedEvent) Data() interface{} { +func (e *RefreshTokenRenewedEvent) Payload() interface{} { return e } -func (e *RefreshTokenRenewedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RefreshTokenRenewedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -215,11 +215,11 @@ type RefreshTokenRevokedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *RefreshTokenRevokedEvent) Data() interface{} { +func (e *RefreshTokenRevokedEvent) Payload() interface{} { return e } -func (e *RefreshTokenRevokedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RefreshTokenRevokedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/org/custom_text.go b/internal/repository/org/custom_text.go index 2e12d5aac..7e6768ee0 100644 --- a/internal/repository/org/custom_text.go +++ b/internal/repository/org/custom_text.go @@ -6,8 +6,6 @@ import ( "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -39,7 +37,7 @@ func NewCustomTextSetEvent( } } -func CustomTextSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextSetEventMapper(event) if err != nil { return nil, err @@ -69,7 +67,7 @@ func NewCustomTextRemovedEvent( } } -func CustomTextRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextRemovedEventMapper(event) if err != nil { return nil, err @@ -97,7 +95,7 @@ func NewCustomTextTemplateRemovedEvent( } } -func CustomTextTemplateRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextTemplateRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.CustomTextTemplateRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/domain.go b/internal/repository/org/domain.go index 56515dc79..348964212 100644 --- a/internal/repository/org/domain.go +++ b/internal/repository/org/domain.go @@ -2,14 +2,11 @@ package org import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -23,15 +20,15 @@ const ( OrgDomainRemovedEventType = domainEventPrefix + "removed" ) -func NewAddOrgDomainUniqueConstraint(orgDomain string) *eventstore.EventUniqueConstraint { +func NewAddOrgDomainUniqueConstraint(orgDomain string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueOrgDomain, orgDomain, "Errors.Org.Domain.AlreadyExists") } -func NewRemoveOrgDomainUniqueConstraint(orgDomain string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveOrgDomainUniqueConstraint(orgDomain string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueOrgDomain, orgDomain) } @@ -42,11 +39,11 @@ type DomainAddedEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainAddedEvent) Data() interface{} { +func (e *DomainAddedEvent) Payload() interface{} { return e } -func (e *DomainAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -61,11 +58,11 @@ func NewDomainAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, d } } -func DomainAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainAdded := &DomainAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainAdded) + err := event.Unmarshal(orgDomainAdded) if err != nil { return nil, errors.ThrowInternal(err, "ORG-GBr52", "unable to unmarshal org domain added") } @@ -81,11 +78,11 @@ type DomainVerificationAddedEvent struct { ValidationCode *crypto.CryptoValue `json:"validationCode,omitempty"` } -func (e *DomainVerificationAddedEvent) Data() interface{} { +func (e *DomainVerificationAddedEvent) Payload() interface{} { return e } -func (e *DomainVerificationAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainVerificationAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -107,11 +104,11 @@ func NewDomainVerificationAddedEvent( } } -func DomainVerificationAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainVerificationAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainVerificationAdded := &DomainVerificationAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainVerificationAdded) + err := event.Unmarshal(orgDomainVerificationAdded) if err != nil { return nil, errors.ThrowInternal(err, "ORG-NRN32", "unable to unmarshal org domain verification added") } @@ -125,11 +122,11 @@ type DomainVerificationFailedEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainVerificationFailedEvent) Data() interface{} { +func (e *DomainVerificationFailedEvent) Payload() interface{} { return e } -func (e *DomainVerificationFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -144,11 +141,11 @@ func NewDomainVerificationFailedEvent(ctx context.Context, aggregate *eventstore } } -func DomainVerificationFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainVerificationFailed := &DomainVerificationFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainVerificationFailed) + err := event.Unmarshal(orgDomainVerificationFailed) if err != nil { return nil, errors.ThrowInternal(err, "ORG-Bhm37", "unable to unmarshal org domain verification failed") } @@ -162,12 +159,12 @@ type DomainVerifiedEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainVerifiedEvent) Data() interface{} { +func (e *DomainVerifiedEvent) Payload() interface{} { return e } -func (e *DomainVerifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddOrgDomainUniqueConstraint(e.Domain)} +func (e *DomainVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddOrgDomainUniqueConstraint(e.Domain)} } func NewDomainVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainVerifiedEvent { @@ -181,11 +178,11 @@ func NewDomainVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate } } -func DomainVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainVerified := &DomainVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainVerified) + err := event.Unmarshal(orgDomainVerified) if err != nil { return nil, errors.ThrowInternal(err, "ORG-BFSwt", "unable to unmarshal org domain verified") } @@ -199,11 +196,11 @@ type DomainPrimarySetEvent struct { Domain string `json:"domain,omitempty"` } -func (e *DomainPrimarySetEvent) Data() interface{} { +func (e *DomainPrimarySetEvent) Payload() interface{} { return e } -func (e *DomainPrimarySetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainPrimarySetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -218,11 +215,11 @@ func NewDomainPrimarySetEvent(ctx context.Context, aggregate *eventstore.Aggrega } } -func DomainPrimarySetEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPrimarySetEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainPrimarySet := &DomainPrimarySetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainPrimarySet) + err := event.Unmarshal(orgDomainPrimarySet) if err != nil { return nil, errors.ThrowInternal(err, "ORG-N5787", "unable to unmarshal org domain primary set") } @@ -237,15 +234,15 @@ type DomainRemovedEvent struct { isVerified bool } -func (e *DomainRemovedEvent) Data() interface{} { +func (e *DomainRemovedEvent) Payload() interface{} { return e } -func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { if !e.isVerified { return nil } - return []*eventstore.EventUniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)} + return []*eventstore.UniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)} } func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string, verified bool) *DomainRemovedEvent { @@ -260,11 +257,11 @@ func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, } } -func DomainRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgDomainRemoved := &DomainRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgDomainRemoved) + err := event.Unmarshal(orgDomainRemoved) if err != nil { return nil, errors.ThrowInternal(err, "ORG-BngB2", "unable to unmarshal org domain removed") } diff --git a/internal/repository/org/flow.go b/internal/repository/org/flow.go index 48a8c360b..cccb6d8e1 100644 --- a/internal/repository/org/flow.go +++ b/internal/repository/org/flow.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/flow" ) @@ -38,7 +37,7 @@ func NewTriggerActionsSetEvent( } } -func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func TriggerActionsSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := flow.TriggerActionsSetEventMapper(event) if err != nil { return nil, err @@ -68,7 +67,7 @@ func NewTriggerActionsCascadeRemovedEvent( } } -func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func TriggerActionsCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := flow.TriggerActionsCascadeRemovedEventMapper(event) if err != nil { return nil, err @@ -96,7 +95,7 @@ func NewFlowClearedEvent( } } -func FlowClearedEventMapper(event *repository.Event) (eventstore.Event, error) { +func FlowClearedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := flow.FlowClearedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index ef590df3f..5f28d4481 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -6,7 +6,6 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/idp" ) @@ -80,7 +79,7 @@ func NewOAuthIDPAddedEvent( } } -func OAuthIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OAuthIDPAddedEventMapper(event) if err != nil { return nil, err @@ -115,7 +114,7 @@ func NewOAuthIDPChangedEvent( return &OAuthIDPChangedEvent{OAuthIDPChangedEvent: *changedEvent}, nil } -func OAuthIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OAuthIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OAuthIDPChangedEventMapper(event) if err != nil { return nil, err @@ -160,7 +159,7 @@ func NewOIDCIDPAddedEvent( } } -func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPAddedEventMapper(event) if err != nil { return nil, err @@ -195,7 +194,7 @@ func NewOIDCIDPChangedEvent( return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *changedEvent}, nil } -func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPChangedEventMapper(event) if err != nil { return nil, err @@ -239,7 +238,7 @@ func NewOIDCIDPMigratedAzureADEvent( } } -func OIDCIDPMigratedAzureADEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedAzureADEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPMigratedAzureADEventMapper(event) if err != nil { return nil, err @@ -279,7 +278,7 @@ func NewOIDCIDPMigratedGoogleEvent( } } -func OIDCIDPMigratedGoogleEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCIDPMigratedGoogleEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.OIDCIDPMigratedGoogleEventMapper(event) if err != nil { return nil, err @@ -322,7 +321,7 @@ func NewJWTIDPAddedEvent( } } -func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.JWTIDPAddedEventMapper(event) if err != nil { return nil, err @@ -357,7 +356,7 @@ func NewJWTIDPChangedEvent( return &JWTIDPChangedEvent{JWTIDPChangedEvent: *changedEvent}, nil } -func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func JWTIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.JWTIDPChangedEventMapper(event) if err != nil { return nil, err @@ -402,7 +401,7 @@ func NewAzureADIDPAddedEvent( } } -func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AzureADIDPAddedEventMapper(event) if err != nil { return nil, err @@ -437,7 +436,7 @@ func NewAzureADIDPChangedEvent( return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *changedEvent}, nil } -func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AzureADIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AzureADIDPChangedEventMapper(event) if err != nil { return nil, err @@ -478,7 +477,7 @@ func NewGitHubIDPAddedEvent( } } -func GitHubIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubIDPAddedEventMapper(event) if err != nil { return nil, err @@ -513,7 +512,7 @@ func NewGitHubIDPChangedEvent( return &GitHubIDPChangedEvent{GitHubIDPChangedEvent: *changedEvent}, nil } -func GitHubIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubIDPChangedEventMapper(event) if err != nil { return nil, err @@ -560,7 +559,7 @@ func NewGitHubEnterpriseIDPAddedEvent( } } -func GitHubEnterpriseIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubEnterpriseIDPAddedEventMapper(event) if err != nil { return nil, err @@ -595,7 +594,7 @@ func NewGitHubEnterpriseIDPChangedEvent( return &GitHubEnterpriseIDPChangedEvent{GitHubEnterpriseIDPChangedEvent: *changedEvent}, nil } -func GitHubEnterpriseIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitHubEnterpriseIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitHubEnterpriseIDPChangedEventMapper(event) if err != nil { return nil, err @@ -636,7 +635,7 @@ func NewGitLabIDPAddedEvent( } } -func GitLabIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabIDPAddedEventMapper(event) if err != nil { return nil, err @@ -671,7 +670,7 @@ func NewGitLabIDPChangedEvent( return &GitLabIDPChangedEvent{GitLabIDPChangedEvent: *changedEvent}, nil } -func GitLabIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabIDPChangedEventMapper(event) if err != nil { return nil, err @@ -714,7 +713,7 @@ func NewGitLabSelfHostedIDPAddedEvent( } } -func GitLabSelfHostedIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabSelfHostedIDPAddedEventMapper(event) if err != nil { return nil, err @@ -749,7 +748,7 @@ func NewGitLabSelfHostedIDPChangedEvent( return &GitLabSelfHostedIDPChangedEvent{GitLabSelfHostedIDPChangedEvent: *changedEvent}, nil } -func GitLabSelfHostedIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GitLabSelfHostedIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GitLabSelfHostedIDPChangedEventMapper(event) if err != nil { return nil, err @@ -790,7 +789,7 @@ func NewGoogleIDPAddedEvent( } } -func GoogleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GoogleIDPAddedEventMapper(event) if err != nil { return nil, err @@ -825,7 +824,7 @@ func NewGoogleIDPChangedEvent( return &GoogleIDPChangedEvent{GoogleIDPChangedEvent: *changedEvent}, nil } -func GoogleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GoogleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.GoogleIDPChangedEventMapper(event) if err != nil { return nil, err @@ -880,7 +879,7 @@ func NewLDAPIDPAddedEvent( } } -func LDAPIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.LDAPIDPAddedEventMapper(event) if err != nil { return nil, err @@ -915,7 +914,7 @@ func NewLDAPIDPChangedEvent( return &LDAPIDPChangedEvent{LDAPIDPChangedEvent: *changedEvent}, nil } -func LDAPIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LDAPIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.LDAPIDPChangedEventMapper(event) if err != nil { return nil, err @@ -960,7 +959,7 @@ func NewAppleIDPAddedEvent( } } -func AppleIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AppleIDPAddedEventMapper(event) if err != nil { return nil, err @@ -995,7 +994,7 @@ func NewAppleIDPChangedEvent( return &AppleIDPChangedEvent{AppleIDPChangedEvent: *changedEvent}, nil } -func AppleIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AppleIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.AppleIDPChangedEventMapper(event) if err != nil { return nil, err @@ -1040,7 +1039,7 @@ func NewSAMLIDPAddedEvent( } } -func SAMLIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.SAMLIDPAddedEventMapper(event) if err != nil { return nil, err @@ -1074,7 +1073,7 @@ func NewSAMLIDPChangedEvent( return &SAMLIDPChangedEvent{SAMLIDPChangedEvent: *changedEvent}, nil } -func SAMLIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLIDPChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.SAMLIDPChangedEventMapper(event) if err != nil { return nil, err @@ -1104,11 +1103,11 @@ func NewIDPRemovedEvent( } } -func (e *IDPRemovedEvent) Data() interface{} { +func (e *IDPRemovedEvent) Payload() interface{} { return e } -func IDPRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idp.RemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/idp_config.go b/internal/repository/org/idp_config.go index 2596cd941..d6e9d5930 100644 --- a/internal/repository/org/idp_config.go +++ b/internal/repository/org/idp_config.go @@ -3,10 +3,8 @@ package org import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -48,7 +46,7 @@ func NewIDPConfigAddedEvent( } } -func IDPConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigAddedEventMapper(event) if err != nil { return nil, err @@ -82,7 +80,7 @@ func NewIDPConfigChangedEvent( return &IDPConfigChangedEvent{IDPConfigChangedEvent: *changeEvent}, nil } -func IDPConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigChangedEventMapper(event) if err != nil { return nil, err @@ -115,7 +113,7 @@ func NewIDPConfigRemovedEvent( } } -func IDPConfigRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigRemovedEventMapper(event) if err != nil { return nil, err @@ -146,7 +144,7 @@ func NewIDPConfigDeactivatedEvent( } } -func IDPConfigDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigDeactivatedEventMapper(event) if err != nil { return nil, err @@ -177,7 +175,7 @@ func NewIDPConfigReactivatedEvent( } } -func IDPConfigReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPConfigReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.IDPConfigReactivatedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/idp_jwt_config.go b/internal/repository/org/idp_jwt_config.go index 866feacc5..ad5378d6a 100644 --- a/internal/repository/org/idp_jwt_config.go +++ b/internal/repository/org/idp_jwt_config.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -44,7 +42,7 @@ func NewIDPJWTConfigAddedEvent( } } -func IDPJWTConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPJWTConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.JWTConfigAddedEventMapper(event) if err != nil { return nil, err @@ -77,7 +75,7 @@ func NewIDPJWTConfigChangedEvent( return &IDPJWTConfigChangedEvent{JWTConfigChangedEvent: *changeEvent}, nil } -func IDPJWTConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPJWTConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.JWTConfigChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/idp_oidc_config.go b/internal/repository/org/idp_oidc_config.go index 45a21332e..70a0421b0 100644 --- a/internal/repository/org/idp_oidc_config.go +++ b/internal/repository/org/idp_oidc_config.go @@ -3,11 +3,9 @@ package org import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/idpconfig" ) @@ -54,7 +52,7 @@ func NewIDPOIDCConfigAddedEvent( } } -func IDPOIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPOIDCConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.OIDCConfigAddedEventMapper(event) if err != nil { return nil, err @@ -87,7 +85,7 @@ func NewIDPOIDCConfigChangedEvent( return &IDPOIDCConfigChangedEvent{OIDCConfigChangedEvent: *changeEvent}, nil } -func IDPOIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IDPOIDCConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := idpconfig.OIDCConfigChangedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/member.go b/internal/repository/org/member.go index 20d909bf9..81a4d5850 100644 --- a/internal/repository/org/member.go +++ b/internal/repository/org/member.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/member" ) @@ -39,7 +37,7 @@ func NewMemberAddedEvent( } } -func MemberAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.MemberAddedEventMapper(event) if err != nil { return nil, err @@ -72,7 +70,7 @@ func NewMemberChangedEvent( } } -func MemberChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.ChangedEventMapper(event) if err != nil { return nil, err @@ -102,7 +100,7 @@ func NewMemberRemovedEvent( } } -func MemberRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.RemovedEventMapper(event) if err != nil { return nil, err @@ -132,7 +130,7 @@ func NewMemberCascadeRemovedEvent( } } -func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.CascadeRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/metadata.go b/internal/repository/org/metadata.go index 04bc64f89..ee92c6f5c 100644 --- a/internal/repository/org/metadata.go +++ b/internal/repository/org/metadata.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/metadata" ) @@ -30,7 +29,7 @@ func NewMetadataSetEvent(ctx context.Context, aggregate *eventstore.Aggregate, k } } -func MetadataSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.SetEventMapper(event) if err != nil { return nil, err @@ -54,7 +53,7 @@ func NewMetadataRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func MetadataRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.RemovedEventMapper(event) if err != nil { return nil, err @@ -78,7 +77,7 @@ func NewMetadataRemovedAllEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func MetadataRemovedAllEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataRemovedAllEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.RemovedAllEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/org.go b/internal/repository/org/org.go index 7657bef49..66bbc59a4 100644 --- a/internal/repository/org/org.go +++ b/internal/repository/org/org.go @@ -2,12 +2,10 @@ package org import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" ) @@ -21,15 +19,15 @@ const ( OrgRemovedEventType = orgEventTypePrefix + "removed" ) -func NewAddOrgNameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { +func NewAddOrgNameUniqueConstraint(orgName string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( uniqueOrgname, orgName, "Errors.Org.AlreadyExists") } -func NewRemoveOrgNameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveOrgNameUniqueConstraint(orgName string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( uniqueOrgname, orgName) } @@ -40,12 +38,12 @@ type OrgAddedEvent struct { Name string `json:"name,omitempty"` } -func (e *OrgAddedEvent) Data() interface{} { +func (e *OrgAddedEvent) Payload() interface{} { return e } -func (e *OrgAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddOrgNameUniqueConstraint(e.Name)} +func (e *OrgAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddOrgNameUniqueConstraint(e.Name)} } func NewOrgAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, name string) *OrgAddedEvent { @@ -59,11 +57,11 @@ func NewOrgAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, name } } -func OrgAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OrgAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgAdded := &OrgAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgAdded) + err := event.Unmarshal(orgAdded) if err != nil { return nil, errors.ThrowInternal(err, "ORG-Bren2", "unable to unmarshal org added") } @@ -78,12 +76,12 @@ type OrgChangedEvent struct { oldName string `json:"-"` } -func (e *OrgChangedEvent) Data() interface{} { +func (e *OrgChangedEvent) Payload() interface{} { return e } -func (e *OrgChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ +func (e *OrgChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ NewRemoveOrgNameUniqueConstraint(e.oldName), NewAddOrgNameUniqueConstraint(e.Name), } @@ -101,11 +99,11 @@ func NewOrgChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, ol } } -func OrgChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OrgChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { orgChanged := &OrgChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, orgChanged) + err := event.Unmarshal(orgChanged) if err != nil { return nil, errors.ThrowInternal(err, "ORG-Bren2", "unable to unmarshal org added") } @@ -117,11 +115,11 @@ type OrgDeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *OrgDeactivatedEvent) Data() interface{} { +func (e *OrgDeactivatedEvent) Payload() interface{} { return e } -func (e *OrgDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OrgDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -135,7 +133,7 @@ func NewOrgDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate } } -func OrgDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OrgDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &OrgDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -145,11 +143,11 @@ type OrgReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *OrgReactivatedEvent) Data() interface{} { +func (e *OrgReactivatedEvent) Payload() interface{} { return e } -func (e *OrgReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OrgReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -163,7 +161,7 @@ func NewOrgReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate } } -func OrgReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OrgReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &OrgReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -179,12 +177,12 @@ type OrgRemovedEvent struct { samlEntityIDs []string } -func (e *OrgRemovedEvent) Data() interface{} { +func (e *OrgRemovedEvent) Payload() interface{} { return nil } -func (e *OrgRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - constraints := []*eventstore.EventUniqueConstraint{ +func (e *OrgRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + constraints := []*eventstore.UniqueConstraint{ NewRemoveOrgNameUniqueConstraint(e.name), } for _, name := range e.usernames { @@ -218,7 +216,7 @@ func NewOrgRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, na } } -func OrgRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OrgRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &OrgRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/org/policy_domain.go b/internal/repository/org/policy_domain.go index 18191533c..352a9e526 100644 --- a/internal/repository/org/policy_domain.go +++ b/internal/repository/org/policy_domain.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -39,7 +37,7 @@ func NewDomainPolicyAddedEvent( } } -func DomainPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.DomainPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -70,7 +68,7 @@ func NewDomainPolicyChangedEvent( return &DomainPolicyChangedEvent{DomainPolicyChangedEvent: *changedEvent}, nil } -func DomainPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.DomainPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -97,7 +95,7 @@ func NewDomainPolicyRemovedEvent( } } -func DomainPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.DomainPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_label.go b/internal/repository/org/policy_label.go index 5a17de6ed..2ca2c874b 100644 --- a/internal/repository/org/policy_label.go +++ b/internal/repository/org/policy_label.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -68,7 +67,7 @@ func NewLabelPolicyAddedEvent( } } -func LabelPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -99,7 +98,7 @@ func NewLabelPolicyChangedEvent( return &LabelPolicyChangedEvent{LabelPolicyChangedEvent: *changedEvent}, nil } -func LabelPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -126,7 +125,7 @@ func NewLabelPolicyRemovedEvent( } } -func LabelPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyRemovedEventMapper(event) if err != nil { return nil, err @@ -153,7 +152,7 @@ func NewLabelPolicyActivatedEvent( } } -func LabelPolicyActivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyActivatedEventMapper(event) if err != nil { return nil, err @@ -182,7 +181,7 @@ func NewLabelPolicyLogoAddedEvent( } } -func LabelPolicyLogoAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoAddedEventMapper(event) if err != nil { return nil, err @@ -211,7 +210,7 @@ func NewLabelPolicyLogoRemovedEvent( } } -func LabelPolicyLogoRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoRemovedEventMapper(event) if err != nil { return nil, err @@ -240,7 +239,7 @@ func NewLabelPolicyIconAddedEvent( } } -func LabelPolicyIconAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconAddedEventMapper(event) if err != nil { return nil, err @@ -269,7 +268,7 @@ func NewLabelPolicyIconRemovedEvent( } } -func LabelPolicyIconRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconRemovedEventMapper(event) if err != nil { return nil, err @@ -298,7 +297,7 @@ func NewLabelPolicyLogoDarkAddedEvent( } } -func LabelPolicyLogoDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoDarkAddedEventMapper(event) if err != nil { return nil, err @@ -327,7 +326,7 @@ func NewLabelPolicyLogoDarkRemovedEvent( } } -func LabelPolicyLogoDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyLogoDarkRemovedEventMapper(event) if err != nil { return nil, err @@ -356,7 +355,7 @@ func NewLabelPolicyIconDarkAddedEvent( } } -func LabelPolicyIconDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconDarkAddedEventMapper(event) if err != nil { return nil, err @@ -385,7 +384,7 @@ func NewLabelPolicyIconDarkRemovedEvent( } } -func LabelPolicyIconDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyIconDarkRemovedEventMapper(event) if err != nil { return nil, err @@ -414,7 +413,7 @@ func NewLabelPolicyFontAddedEvent( } } -func LabelPolicyFontAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyFontAddedEventMapper(event) if err != nil { return nil, err @@ -443,7 +442,7 @@ func NewLabelPolicyFontRemovedEvent( } } -func LabelPolicyFontRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyFontRemovedEventMapper(event) if err != nil { return nil, err @@ -456,11 +455,11 @@ type LabelPolicyAssetsRemovedEvent struct { policy.LabelPolicyAssetsRemovedEvent } -func (e *LabelPolicyAssetsRemovedEvent) Data() interface{} { +func (e *LabelPolicyAssetsRemovedEvent) Payload() interface{} { return nil } -func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -478,7 +477,7 @@ func NewLabelPolicyAssetsRemovedEvent( } } -func LabelPolicyAssetsRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAssetsRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LabelPolicyAssetsRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_login.go b/internal/repository/org/policy_login.go index 95e38ca92..af3701791 100644 --- a/internal/repository/org/policy_login.go +++ b/internal/repository/org/policy_login.go @@ -4,10 +4,8 @@ import ( "context" "time" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -69,7 +67,7 @@ func NewLoginPolicyAddedEvent( } } -func LoginPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LoginPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -100,7 +98,7 @@ func NewLoginPolicyChangedEvent( return &LoginPolicyChangedEvent{LoginPolicyChangedEvent: *changedEvent}, nil } -func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LoginPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -127,7 +125,7 @@ func NewLoginPolicyRemovedEvent( } } -func LoginPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LoginPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_login_factors.go b/internal/repository/org/policy_login_factors.go index 568c3ed8c..c2322b9d2 100644 --- a/internal/repository/org/policy_login_factors.go +++ b/internal/repository/org/policy_login_factors.go @@ -3,10 +3,8 @@ package org import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -37,7 +35,7 @@ func NewLoginPolicySecondFactorAddedEvent( } } -func SecondFactorAddedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorAddedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.SecondFactorAddedEventMapper(event) if err != nil { return nil, err @@ -68,7 +66,7 @@ func NewLoginPolicySecondFactorRemovedEvent( } } -func SecondFactorRemovedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorRemovedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.SecondFactorRemovedEventMapper(event) if err != nil { return nil, err @@ -98,7 +96,7 @@ func NewLoginPolicyMultiFactorAddedEvent( } } -func MultiFactorAddedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorAddedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MultiFactorAddedEventMapper(event) if err != nil { return nil, err @@ -129,7 +127,7 @@ func NewLoginPolicyMultiFactorRemovedEvent( } } -func MultiFactorRemovedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorRemovedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MultiFactorRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_login_identity_provider.go b/internal/repository/org/policy_login_identity_provider.go index ea963a99f..13f8bc35b 100644 --- a/internal/repository/org/policy_login_identity_provider.go +++ b/internal/repository/org/policy_login_identity_provider.go @@ -3,10 +3,8 @@ package org import ( "context" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -38,7 +36,7 @@ func NewIdentityProviderAddedEvent( } } -func IdentityProviderAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderAddedEventMapper(event) if err != nil { return nil, err @@ -68,7 +66,7 @@ func NewIdentityProviderRemovedEvent( } } -func IdentityProviderRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderRemovedEventMapper(event) if err != nil { return nil, err @@ -95,7 +93,7 @@ func NewIdentityProviderCascadeRemovedEvent( } } -func IdentityProviderCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.IdentityProviderCascadeRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_mail_template.go b/internal/repository/org/policy_mail_template.go index 7c4270205..288d94d45 100644 --- a/internal/repository/org/policy_mail_template.go +++ b/internal/repository/org/policy_mail_template.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -31,7 +29,7 @@ func NewMailTemplateAddedEvent( } } -func MailTemplateAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTemplateAddedEventMapper(event) if err != nil { return nil, err @@ -59,7 +57,7 @@ func NewMailTemplateChangedEvent( return &MailTemplateChangedEvent{MailTemplateChangedEvent: *changedEvent}, nil } -func MailTemplateChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTemplateChangedEventMapper(event) if err != nil { return nil, err @@ -83,7 +81,7 @@ func NewMailTemplateRemovedEvent( } } -func MailTemplateRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTemplateRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_mail_text.go b/internal/repository/org/policy_mail_text.go index 826dae46c..4d541c809 100644 --- a/internal/repository/org/policy_mail_text.go +++ b/internal/repository/org/policy_mail_text.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -45,7 +43,7 @@ func NewMailTextAddedEvent( } } -func MailTextAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTextAddedEventMapper(event) if err != nil { return nil, err @@ -77,7 +75,7 @@ func NewMailTextChangedEvent( return &MailTextChangedEvent{MailTextChangedEvent: *changedEvent}, nil } -func MailTextChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTextChangedEventMapper(event) if err != nil { return nil, err @@ -105,7 +103,7 @@ func NewMailTextRemovedEvent( } } -func MailTextRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.MailTextRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_notification.go b/internal/repository/org/policy_notification.go index 0c436e902..5beb9046e 100644 --- a/internal/repository/org/policy_notification.go +++ b/internal/repository/org/policy_notification.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -34,7 +33,7 @@ func NewNotificationPolicyAddedEvent( } } -func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.NotificationPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -65,7 +64,7 @@ func NewNotificationPolicyChangedEvent( return &NotificationPolicyChangedEvent{NotificationPolicyChangedEvent: *changedEvent}, nil } -func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.NotificationPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -92,7 +91,7 @@ func NewNotificationPolicyRemovedEvent( } } -func NotificationPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.NotificationPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_password_age.go b/internal/repository/org/policy_password_age.go index 8268fe50b..99fc812c1 100644 --- a/internal/repository/org/policy_password_age.go +++ b/internal/repository/org/policy_password_age.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -36,7 +34,7 @@ func NewPasswordAgePolicyAddedEvent( } } -func PasswordAgePolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordAgePolicyAddedEventMapper(event) if err != nil { return nil, err @@ -67,7 +65,7 @@ func NewPasswordAgePolicyChangedEvent( return &PasswordAgePolicyChangedEvent{PasswordAgePolicyChangedEvent: *changedEvent}, nil } -func PasswordAgePolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordAgePolicyChangedEventMapper(event) if err != nil { return nil, err @@ -94,7 +92,7 @@ func NewPasswordAgePolicyRemovedEvent( } } -func PasswordAgePolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordAgePolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_password_complexity.go b/internal/repository/org/policy_password_complexity.go index 624062566..be9054537 100644 --- a/internal/repository/org/policy_password_complexity.go +++ b/internal/repository/org/policy_password_complexity.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -42,7 +40,7 @@ func NewPasswordComplexityPolicyAddedEvent( } } -func PasswordComplexityPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordComplexityPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -73,7 +71,7 @@ func NewPasswordComplexityPolicyChangedEvent( return &PasswordComplexityPolicyChangedEvent{PasswordComplexityPolicyChangedEvent: *changedEvent}, nil } -func PasswordComplexityPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordComplexityPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -100,7 +98,7 @@ func NewPasswordComplexityPolicyRemovedEvent( } } -func PasswordComplexityPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PasswordComplexityPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_password_lockout.go b/internal/repository/org/policy_password_lockout.go index 8536a924c..e95d86eb7 100644 --- a/internal/repository/org/policy_password_lockout.go +++ b/internal/repository/org/policy_password_lockout.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -36,7 +34,7 @@ func NewLockoutPolicyAddedEvent( } } -func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LockoutPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -67,7 +65,7 @@ func NewLockoutPolicyChangedEvent( return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *changedEvent}, nil } -func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LockoutPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -94,7 +92,7 @@ func NewLockoutPolicyRemovedEvent( } } -func LockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.LockoutPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/org/policy_privacy.go b/internal/repository/org/policy_privacy.go index 380711a3f..98916b112 100644 --- a/internal/repository/org/policy_privacy.go +++ b/internal/repository/org/policy_privacy.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/policy" ) @@ -40,7 +39,7 @@ func NewPrivacyPolicyAddedEvent( } } -func PrivacyPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PrivacyPolicyAddedEventMapper(event) if err != nil { return nil, err @@ -71,7 +70,7 @@ func NewPrivacyPolicyChangedEvent( return &PrivacyPolicyChangedEvent{PrivacyPolicyChangedEvent: *changedEvent}, nil } -func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PrivacyPolicyChangedEventMapper(event) if err != nil { return nil, err @@ -98,7 +97,7 @@ func NewPrivacyPolicyRemovedEvent( } } -func PrivacyPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := policy.PrivacyPolicyRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/policy/custom_text.go b/internal/repository/policy/custom_text.go index f227bcb90..4f3272bbe 100644 --- a/internal/repository/policy/custom_text.go +++ b/internal/repository/policy/custom_text.go @@ -1,13 +1,10 @@ package policy import ( - "encoding/json" - "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -26,11 +23,11 @@ type CustomTextSetEvent struct { Text string `json:"text,omitempty"` } -func (e *CustomTextSetEvent) Data() interface{} { +func (e *CustomTextSetEvent) Payload() interface{} { return e } -func (e *CustomTextSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CustomTextSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -50,12 +47,12 @@ func NewCustomTextSetEvent( } } -func CustomTextSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &CustomTextSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "TEXT-28dwe", "unable to unmarshal custom text") } @@ -71,11 +68,11 @@ type CustomTextRemovedEvent struct { Language language.Tag `json:"language,omitempty"` } -func (e *CustomTextRemovedEvent) Data() interface{} { +func (e *CustomTextRemovedEvent) Payload() interface{} { return e } -func (e *CustomTextRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CustomTextRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -88,12 +85,12 @@ func NewCustomTextRemovedEvent(base *eventstore.BaseEvent, template, key string, } } -func CustomTextRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &CustomTextRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "TEXT-28sMf", "unable to unmarshal custom text removed") } @@ -108,11 +105,11 @@ type CustomTextTemplateRemovedEvent struct { Language language.Tag `json:"language,omitempty"` } -func (e *CustomTextTemplateRemovedEvent) Data() interface{} { +func (e *CustomTextTemplateRemovedEvent) Payload() interface{} { return e } -func (e *CustomTextTemplateRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *CustomTextTemplateRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -124,12 +121,12 @@ func NewCustomTextTemplateRemovedEvent(base *eventstore.BaseEvent, template stri } } -func CustomTextTemplateRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func CustomTextTemplateRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &CustomTextTemplateRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "TEXT-mKKRs", "unable to unmarshal custom text message removed") } diff --git a/internal/repository/policy/label.go b/internal/repository/policy/label.go index 754a65e0b..39d637918 100644 --- a/internal/repository/policy/label.go +++ b/internal/repository/policy/label.go @@ -1,11 +1,8 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/asset" ) @@ -48,11 +45,11 @@ type LabelPolicyAddedEvent struct { DisableWatermark bool `json:"disableMsgPopup,omitempty"` } -func (e *LabelPolicyAddedEvent) Data() interface{} { +func (e *LabelPolicyAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -87,12 +84,12 @@ func NewLabelPolicyAddedEvent( } } -func LabelPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LabelPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-puqv4", "unable to unmarshal label policy") } @@ -116,11 +113,11 @@ type LabelPolicyChangedEvent struct { DisableWatermark *bool `json:"disableWatermark,omitempty"` } -func (e *LabelPolicyChangedEvent) Data() interface{} { +func (e *LabelPolicyChangedEvent) Payload() interface{} { return e } -func (e *LabelPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -208,12 +205,12 @@ func ChangeDisableWatermark(disableWatermark bool) func(*LabelPolicyChangedEvent } } -func LabelPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LabelPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-qhfFb", "unable to unmarshal label policy") } @@ -225,11 +222,11 @@ type LabelPolicyActivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *LabelPolicyActivatedEvent) Data() interface{} { +func (e *LabelPolicyActivatedEvent) Payload() interface{} { return e } -func (e *LabelPolicyActivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyActivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -239,7 +236,7 @@ func NewLabelPolicyActivatedEvent(base *eventstore.BaseEvent) *LabelPolicyActiva } } -func LabelPolicyActivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &LabelPolicyActivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -249,11 +246,11 @@ type LabelPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *LabelPolicyRemovedEvent) Data() interface{} { +func (e *LabelPolicyRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -263,7 +260,7 @@ func NewLabelPolicyRemovedEvent(base *eventstore.BaseEvent) *LabelPolicyRemovedE } } -func LabelPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &LabelPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -273,11 +270,11 @@ type LabelPolicyLogoAddedEvent struct { asset.AddedEvent } -func (e *LabelPolicyLogoAddedEvent) Data() interface{} { +func (e *LabelPolicyLogoAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyLogoAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyLogoAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -287,7 +284,7 @@ func NewLabelPolicyLogoAddedEvent(base *eventstore.BaseEvent, storageKey string) } } -func LabelPolicyLogoAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -300,11 +297,11 @@ type LabelPolicyLogoRemovedEvent struct { asset.RemovedEvent } -func (e *LabelPolicyLogoRemovedEvent) Data() interface{} { +func (e *LabelPolicyLogoRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyLogoRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyLogoRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -314,7 +311,7 @@ func NewLabelPolicyLogoRemovedEvent(base *eventstore.BaseEvent, storageKey strin } } -func LabelPolicyLogoRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err @@ -327,11 +324,11 @@ type LabelPolicyIconAddedEvent struct { asset.AddedEvent } -func (e *LabelPolicyIconAddedEvent) Data() interface{} { +func (e *LabelPolicyIconAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyIconAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyIconAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -341,7 +338,7 @@ func NewLabelPolicyIconAddedEvent(base *eventstore.BaseEvent, storageKey string) } } -func LabelPolicyIconAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -354,11 +351,11 @@ type LabelPolicyIconRemovedEvent struct { asset.RemovedEvent } -func (e *LabelPolicyIconRemovedEvent) Data() interface{} { +func (e *LabelPolicyIconRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyIconRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyIconRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -368,7 +365,7 @@ func NewLabelPolicyIconRemovedEvent(base *eventstore.BaseEvent, storageKey strin } } -func LabelPolicyIconRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err @@ -381,11 +378,11 @@ type LabelPolicyLogoDarkAddedEvent struct { asset.AddedEvent } -func (e *LabelPolicyLogoDarkAddedEvent) Data() interface{} { +func (e *LabelPolicyLogoDarkAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyLogoDarkAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyLogoDarkAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -395,7 +392,7 @@ func NewLabelPolicyLogoDarkAddedEvent(base *eventstore.BaseEvent, storageKey str } } -func LabelPolicyLogoDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -408,11 +405,11 @@ type LabelPolicyLogoDarkRemovedEvent struct { asset.RemovedEvent } -func (e *LabelPolicyLogoDarkRemovedEvent) Data() interface{} { +func (e *LabelPolicyLogoDarkRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyLogoDarkRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyLogoDarkRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -422,7 +419,7 @@ func NewLabelPolicyLogoDarkRemovedEvent(base *eventstore.BaseEvent, storageKey s } } -func LabelPolicyLogoDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyLogoDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err @@ -435,11 +432,11 @@ type LabelPolicyIconDarkAddedEvent struct { asset.AddedEvent } -func (e *LabelPolicyIconDarkAddedEvent) Data() interface{} { +func (e *LabelPolicyIconDarkAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyIconDarkAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyIconDarkAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -449,7 +446,7 @@ func NewLabelPolicyIconDarkAddedEvent(base *eventstore.BaseEvent, storageKey str } } -func LabelPolicyIconDarkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -462,11 +459,11 @@ type LabelPolicyIconDarkRemovedEvent struct { asset.RemovedEvent } -func (e *LabelPolicyIconDarkRemovedEvent) Data() interface{} { +func (e *LabelPolicyIconDarkRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyIconDarkRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyIconDarkRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -476,7 +473,7 @@ func NewLabelPolicyIconDarkRemovedEvent(base *eventstore.BaseEvent, storageKey s } } -func LabelPolicyIconDarkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyIconDarkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err @@ -489,11 +486,11 @@ type LabelPolicyFontAddedEvent struct { asset.AddedEvent } -func (e *LabelPolicyFontAddedEvent) Data() interface{} { +func (e *LabelPolicyFontAddedEvent) Payload() interface{} { return e } -func (e *LabelPolicyFontAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyFontAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -503,7 +500,7 @@ func NewLabelPolicyFontAddedEvent(base *eventstore.BaseEvent, storageKey string) } } -func LabelPolicyFontAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -516,11 +513,11 @@ type LabelPolicyFontRemovedEvent struct { asset.RemovedEvent } -func (e *LabelPolicyFontRemovedEvent) Data() interface{} { +func (e *LabelPolicyFontRemovedEvent) Payload() interface{} { return e } -func (e *LabelPolicyFontRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyFontRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -530,7 +527,7 @@ func NewLabelPolicyFontRemovedEvent(base *eventstore.BaseEvent, storageKey strin } } -func LabelPolicyFontRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyFontRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err @@ -543,11 +540,11 @@ type LabelPolicyAssetsRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *LabelPolicyAssetsRemovedEvent) Data() interface{} { +func (e *LabelPolicyAssetsRemovedEvent) Payload() interface{} { return nil } -func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LabelPolicyAssetsRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -557,7 +554,7 @@ func NewLabelPolicyAssetsRemovedEvent(base *eventstore.BaseEvent) *LabelPolicyAs } } -func LabelPolicyAssetsRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LabelPolicyAssetsRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &LabelPolicyAssetsRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/login.go b/internal/repository/policy/login.go index 790b1a571..f5e361855 100644 --- a/internal/repository/policy/login.go +++ b/internal/repository/policy/login.go @@ -1,13 +1,11 @@ package policy import ( - "encoding/json" "time" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -39,11 +37,11 @@ type LoginPolicyAddedEvent struct { MultiFactorCheckLifetime time.Duration `json:"multiFactorCheckLifetime,omitempty"` } -func (e *LoginPolicyAddedEvent) Data() interface{} { +func (e *LoginPolicyAddedEvent) Payload() interface{} { return e } -func (e *LoginPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LoginPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -89,12 +87,12 @@ func NewLoginPolicyAddedEvent( } } -func LoginPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LoginPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-nWndT", "unable to unmarshal policy") } @@ -124,11 +122,11 @@ type LoginPolicyChangedEvent struct { MultiFactorCheckLifetime *time.Duration `json:"multiFactorCheckLifetime,omitempty"` } -func (e *LoginPolicyChangedEvent) Data() interface{} { +func (e *LoginPolicyChangedEvent) Payload() interface{} { return e } -func (e *LoginPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LoginPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -252,12 +250,12 @@ func ChangeDisableLoginWithPhone(DisableLoginWithPhone bool) func(*LoginPolicyCh } } -func LoginPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LoginPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-ehssl", "unable to unmarshal policy") } @@ -269,11 +267,11 @@ type LoginPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *LoginPolicyRemovedEvent) Data() interface{} { +func (e *LoginPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *LoginPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LoginPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -283,7 +281,7 @@ func NewLoginPolicyRemovedEvent(base *eventstore.BaseEvent) *LoginPolicyRemovedE } } -func LoginPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LoginPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &LoginPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/mail_template.go b/internal/repository/policy/mail_template.go index 777157bad..3b02c0f4f 100644 --- a/internal/repository/policy/mail_template.go +++ b/internal/repository/policy/mail_template.go @@ -1,11 +1,8 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -22,11 +19,11 @@ type MailTemplateAddedEvent struct { Template []byte `json:"template,omitempty"` } -func (e *MailTemplateAddedEvent) Data() interface{} { +func (e *MailTemplateAddedEvent) Payload() interface{} { return e } -func (e *MailTemplateAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MailTemplateAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -40,12 +37,12 @@ func NewMailTemplateAddedEvent( } } -func MailTemplateAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MailTemplateAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-5m9if", "unable to unmarshal mail template") } @@ -59,11 +56,11 @@ type MailTemplateChangedEvent struct { Template *[]byte `json:"template,omitempty"` } -func (e *MailTemplateChangedEvent) Data() interface{} { +func (e *MailTemplateChangedEvent) Payload() interface{} { return e } -func (e *MailTemplateChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MailTemplateChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -91,12 +88,12 @@ func ChangeTemplate(template []byte) func(*MailTemplateChangedEvent) { } } -func MailTemplateChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MailTemplateChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-3uu8K", "unable to unmarshal mail template policy") } @@ -108,11 +105,11 @@ type MailTemplateRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *MailTemplateRemovedEvent) Data() interface{} { +func (e *MailTemplateRemovedEvent) Payload() interface{} { return nil } -func (e *MailTemplateRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MailTemplateRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -122,7 +119,7 @@ func NewMailTemplateRemovedEvent(base *eventstore.BaseEvent) *MailTemplateRemove } } -func MailTemplateRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTemplateRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &MailTemplateRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/mail_text.go b/internal/repository/policy/mail_text.go index ab6c6d269..04e3363ab 100644 --- a/internal/repository/policy/mail_text.go +++ b/internal/repository/policy/mail_text.go @@ -1,12 +1,10 @@ package policy import ( - "encoding/json" "fmt" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -17,15 +15,15 @@ const ( MailTextPolicyRemovedEventType = mailTextPolicyPrefix + "removed" ) -func NewAddMailTextUniqueConstraint(aggregateID, mailTextType, langugage string) *eventstore.EventUniqueConstraint { +func NewAddMailTextUniqueConstraint(aggregateID, mailTextType, langugage string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueMailText, fmt.Sprintf("%v:%v:%v", aggregateID, mailTextType, langugage), "Errors.Org.AlreadyExists") } -func NewRemoveMailTextUniqueConstraint(aggregateID, mailTextType, langugage string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveMailTextUniqueConstraint(aggregateID, mailTextType, langugage string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueMailText, fmt.Sprintf("%v:%v:%v", aggregateID, mailTextType, langugage)) } @@ -43,12 +41,12 @@ type MailTextAddedEvent struct { ButtonText string `json:"buttonText,omitempty"` } -func (e *MailTextAddedEvent) Data() interface{} { +func (e *MailTextAddedEvent) Payload() interface{} { return e } -func (e *MailTextAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddMailTextUniqueConstraint(e.Aggregate().ResourceOwner, e.MailTextType, e.Language)} +func (e *MailTextAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddMailTextUniqueConstraint(e.Aggregate().ResourceOwner, e.MailTextType, e.Language)} } func NewMailTextAddedEvent( @@ -75,12 +73,12 @@ func NewMailTextAddedEvent( } } -func MailTextAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MailTextAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-5m9if", "unable to unmarshal mail text policy") } @@ -101,11 +99,11 @@ type MailTextChangedEvent struct { ButtonText *string `json:"buttonText,omitempty"` } -func (e *MailTextChangedEvent) Data() interface{} { +func (e *MailTextChangedEvent) Payload() interface{} { return e } -func (e *MailTextChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MailTextChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -167,12 +165,12 @@ func ChangeButtonText(buttonText string) func(*MailTextChangedEvent) { } } -func MailTextChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MailTextChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-bn88u", "unable to unmarshal mail text policy") } @@ -187,12 +185,12 @@ type MailTextRemovedEvent struct { Language string `json:"language,omitempty"` } -func (e *MailTextRemovedEvent) Data() interface{} { +func (e *MailTextRemovedEvent) Payload() interface{} { return nil } -func (e *MailTextRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveMailTextUniqueConstraint(e.Aggregate().ResourceOwner, e.MailTextType, e.Language)} +func (e *MailTextRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveMailTextUniqueConstraint(e.Aggregate().ResourceOwner, e.MailTextType, e.Language)} } func NewMailTextRemovedEvent(base *eventstore.BaseEvent, mailTextType, language string) *MailTextRemovedEvent { @@ -203,7 +201,7 @@ func NewMailTextRemovedEvent(base *eventstore.BaseEvent, mailTextType, language } } -func MailTextRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MailTextRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &MailTextRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_domain.go b/internal/repository/policy/policy_domain.go index b6475535e..607fa5e4a 100644 --- a/internal/repository/policy/policy_domain.go +++ b/internal/repository/policy/policy_domain.go @@ -1,12 +1,8 @@ package policy import ( - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -23,11 +19,11 @@ type DomainPolicyAddedEvent struct { SMTPSenderAddressMatchesInstanceDomain bool `json:"smtpSenderAddressMatchesInstanceDomain,omitempty"` } -func (e *DomainPolicyAddedEvent) Data() interface{} { +func (e *DomainPolicyAddedEvent) Payload() interface{} { return e } -func (e *DomainPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -46,12 +42,12 @@ func NewDomainPolicyAddedEvent( } } -func DomainPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &DomainPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-TvSmA", "unable to unmarshal policy") } @@ -67,11 +63,11 @@ type DomainPolicyChangedEvent struct { SMTPSenderAddressMatchesInstanceDomain *bool `json:"smtpSenderAddressMatchesInstanceDomain,omitempty"` } -func (e *DomainPolicyChangedEvent) Data() interface{} { +func (e *DomainPolicyChangedEvent) Payload() interface{} { return e } -func (e *DomainPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -111,12 +107,12 @@ func ChangeSMTPSenderAddressMatchesInstanceDomain(smtpSenderAddressMatchesInstan } } -func DomainPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &DomainPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-0Pl9d", "unable to unmarshal policy") } @@ -128,11 +124,11 @@ type DomainPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *DomainPolicyRemovedEvent) Data() interface{} { +func (e *DomainPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *DomainPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -142,7 +138,7 @@ func NewDomainPolicyRemovedEvent(base *eventstore.BaseEvent) *DomainPolicyRemove } } -func DomainPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &DomainPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_login_factors.go b/internal/repository/policy/policy_login_factors.go index 5f45725cb..a2749eb5f 100644 --- a/internal/repository/policy/policy_login_factors.go +++ b/internal/repository/policy/policy_login_factors.go @@ -1,12 +1,9 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -35,12 +32,12 @@ func NewSecondFactorAddedEvent( } } -func SecondFactorAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SecondFactorAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-Lp0dE", "unable to unmarshal policy") } @@ -48,11 +45,11 @@ func SecondFactorAddedEventMapper(event *repository.Event) (eventstore.Event, er return e, nil } -func (e *SecondFactorAddedEvent) Data() interface{} { +func (e *SecondFactorAddedEvent) Payload() interface{} { return e } -func (e *SecondFactorAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SecondFactorAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -71,12 +68,12 @@ func NewSecondFactorRemovedEvent( } } -func SecondFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SecondFactorRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SecondFactorRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-5M9gd", "unable to unmarshal policy") } @@ -84,11 +81,11 @@ func SecondFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, return e, nil } -func (e *SecondFactorRemovedEvent) Data() interface{} { +func (e *SecondFactorRemovedEvent) Payload() interface{} { return e } -func (e *SecondFactorRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SecondFactorRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -108,12 +105,12 @@ func NewMultiFactorAddedEvent( } } -func MultiFactorAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MultiFactorAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-5Ms90", "unable to unmarshal policy") } @@ -121,11 +118,11 @@ func MultiFactorAddedEventMapper(event *repository.Event) (eventstore.Event, err return e, nil } -func (e *MultiFactorAddedEvent) Data() interface{} { +func (e *MultiFactorAddedEvent) Payload() interface{} { return e } -func (e *MultiFactorAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MultiFactorAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -144,12 +141,12 @@ func NewMultiFactorRemovedEvent( } } -func MultiFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MultiFactorRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &MultiFactorRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-1N8sd", "unable to unmarshal policy") } @@ -157,10 +154,10 @@ func MultiFactorRemovedEventMapper(event *repository.Event) (eventstore.Event, e return e, nil } -func (e *MultiFactorRemovedEvent) Data() interface{} { +func (e *MultiFactorRemovedEvent) Payload() interface{} { return e } -func (e *MultiFactorRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MultiFactorRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/policy/policy_login_identity_provider.go b/internal/repository/policy/policy_login_identity_provider.go index f3ff719c2..59772190f 100644 --- a/internal/repository/policy/policy_login_identity_provider.go +++ b/internal/repository/policy/policy_login_identity_provider.go @@ -1,12 +1,9 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -23,11 +20,11 @@ type IdentityProviderAddedEvent struct { IDPProviderType domain.IdentityProviderType `json:"idpProviderType,omitempty"` } -func (e *IdentityProviderAddedEvent) Data() interface{} { +func (e *IdentityProviderAddedEvent) Payload() interface{} { return e } -func (e *IdentityProviderAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IdentityProviderAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -44,12 +41,12 @@ func NewIdentityProviderAddedEvent( } } -func IdentityProviderAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IdentityProviderAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROVI-bfNnp", "Errors.Internal") } @@ -63,11 +60,11 @@ type IdentityProviderRemovedEvent struct { IDPConfigID string `json:"idpConfigId"` } -func (e *IdentityProviderRemovedEvent) Data() interface{} { +func (e *IdentityProviderRemovedEvent) Payload() interface{} { return e } -func (e *IdentityProviderRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IdentityProviderRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -81,12 +78,12 @@ func NewIdentityProviderRemovedEvent( } } -func IdentityProviderRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IdentityProviderRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROVI-6H0KQ", "Errors.Internal") } @@ -100,11 +97,11 @@ type IdentityProviderCascadeRemovedEvent struct { IDPConfigID string `json:"idpConfigId"` } -func (e *IdentityProviderCascadeRemovedEvent) Data() interface{} { +func (e *IdentityProviderCascadeRemovedEvent) Payload() interface{} { return e } -func (e *IdentityProviderCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IdentityProviderCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -118,12 +115,12 @@ func NewIdentityProviderCascadeRemovedEvent( } } -func IdentityProviderCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IdentityProviderCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &IdentityProviderCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROVI-7M9fs", "Errors.Internal") } diff --git a/internal/repository/policy/policy_notification.go b/internal/repository/policy/policy_notification.go index 0efb12a12..3ce617e78 100644 --- a/internal/repository/policy/policy_notification.go +++ b/internal/repository/policy/policy_notification.go @@ -1,11 +1,8 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -20,11 +17,11 @@ type NotificationPolicyAddedEvent struct { PasswordChange bool `json:"passwordChange,omitempty"` } -func (e *NotificationPolicyAddedEvent) Data() interface{} { +func (e *NotificationPolicyAddedEvent) Payload() interface{} { return e } -func (e *NotificationPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *NotificationPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -38,12 +35,12 @@ func NewNotificationPolicyAddedEvent( } } -func NotificationPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &NotificationPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-0sp2nios", "unable to unmarshal policy") } @@ -57,11 +54,11 @@ type NotificationPolicyChangedEvent struct { PasswordChange *bool `json:"passwordChange,omitempty"` } -func (e *NotificationPolicyChangedEvent) Data() interface{} { +func (e *NotificationPolicyChangedEvent) Payload() interface{} { return e } -func (e *NotificationPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *NotificationPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -89,12 +86,12 @@ func ChangePasswordChange(passwordChange bool) func(*NotificationPolicyChangedEv } } -func NotificationPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &NotificationPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-09s2oss", "unable to unmarshal policy") } @@ -106,11 +103,11 @@ type NotificationPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *NotificationPolicyRemovedEvent) Data() interface{} { +func (e *NotificationPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *NotificationPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *NotificationPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -120,7 +117,7 @@ func NewNotificationPolicyRemovedEvent(base *eventstore.BaseEvent) *Notification } } -func NotificationPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &NotificationPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_password_age.go b/internal/repository/policy/policy_password_age.go index f994e08ab..ee2ea88a3 100644 --- a/internal/repository/policy/policy_password_age.go +++ b/internal/repository/policy/policy_password_age.go @@ -1,11 +1,8 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -21,11 +18,11 @@ type PasswordAgePolicyAddedEvent struct { MaxAgeDays uint64 `json:"maxAgeDays,omitempty"` } -func (e *PasswordAgePolicyAddedEvent) Data() interface{} { +func (e *PasswordAgePolicyAddedEvent) Payload() interface{} { return e } -func (e *PasswordAgePolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordAgePolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -42,12 +39,12 @@ func NewPasswordAgePolicyAddedEvent( } } -func PasswordAgePolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PasswordAgePolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-T3mGp", "unable to unmarshal policy") } @@ -62,11 +59,11 @@ type PasswordAgePolicyChangedEvent struct { MaxAgeDays *uint64 `json:"maxAgeDays,omitempty"` } -func (e *PasswordAgePolicyChangedEvent) Data() interface{} { +func (e *PasswordAgePolicyChangedEvent) Payload() interface{} { return e } -func (e *PasswordAgePolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordAgePolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -100,12 +97,12 @@ func ChangeMaxAgeDays(maxAgeDays uint64) func(*PasswordAgePolicyChangedEvent) { } } -func PasswordAgePolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PasswordAgePolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-PqaVq", "unable to unmarshal policy") } @@ -117,11 +114,11 @@ type PasswordAgePolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *PasswordAgePolicyRemovedEvent) Data() interface{} { +func (e *PasswordAgePolicyRemovedEvent) Payload() interface{} { return nil } -func (e *PasswordAgePolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordAgePolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -131,7 +128,7 @@ func NewPasswordAgePolicyRemovedEvent(base *eventstore.BaseEvent) *PasswordAgePo } } -func PasswordAgePolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordAgePolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &PasswordAgePolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_password_complexity.go b/internal/repository/policy/policy_password_complexity.go index a7bf665c0..00a35da80 100644 --- a/internal/repository/policy/policy_password_complexity.go +++ b/internal/repository/policy/policy_password_complexity.go @@ -1,12 +1,8 @@ package policy import ( - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -25,11 +21,11 @@ type PasswordComplexityPolicyAddedEvent struct { HasSymbol bool `json:"hasSymbol,omitempty"` } -func (e *PasswordComplexityPolicyAddedEvent) Data() interface{} { +func (e *PasswordComplexityPolicyAddedEvent) Payload() interface{} { return e } -func (e *PasswordComplexityPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordComplexityPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -51,12 +47,12 @@ func NewPasswordComplexityPolicyAddedEvent( } } -func PasswordComplexityPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PasswordComplexityPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-wYxlM", "unable to unmarshal policy") } @@ -74,11 +70,11 @@ type PasswordComplexityPolicyChangedEvent struct { HasSymbol *bool `json:"hasSymbol,omitempty"` } -func (e *PasswordComplexityPolicyChangedEvent) Data() interface{} { +func (e *PasswordComplexityPolicyChangedEvent) Payload() interface{} { return e } -func (e *PasswordComplexityPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordComplexityPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -130,12 +126,12 @@ func ChangeHasSymbol(hasSymbol bool) func(*PasswordComplexityPolicyChangedEvent) } } -func PasswordComplexityPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PasswordComplexityPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-zBGB0", "unable to unmarshal policy") } @@ -147,11 +143,11 @@ type PasswordComplexityPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *PasswordComplexityPolicyRemovedEvent) Data() interface{} { +func (e *PasswordComplexityPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *PasswordComplexityPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordComplexityPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -161,7 +157,7 @@ func NewPasswordComplexityPolicyRemovedEvent(base *eventstore.BaseEvent) *Passwo } } -func PasswordComplexityPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordComplexityPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &PasswordComplexityPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_password_lockout.go b/internal/repository/policy/policy_password_lockout.go index cc257dc80..5e1c1b398 100644 --- a/internal/repository/policy/policy_password_lockout.go +++ b/internal/repository/policy/policy_password_lockout.go @@ -1,12 +1,8 @@ package policy import ( - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -22,11 +18,11 @@ type LockoutPolicyAddedEvent struct { ShowLockOutFailures bool `json:"showLockOutFailures,omitempty"` } -func (e *LockoutPolicyAddedEvent) Data() interface{} { +func (e *LockoutPolicyAddedEvent) Payload() interface{} { return e } -func (e *LockoutPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LockoutPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -43,12 +39,12 @@ func NewLockoutPolicyAddedEvent( } } -func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LockoutPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-8XiVd", "unable to unmarshal policy") } @@ -63,11 +59,11 @@ type LockoutPolicyChangedEvent struct { ShowLockOutFailures *bool `json:"showLockOutFailures,omitempty"` } -func (e *LockoutPolicyChangedEvent) Data() interface{} { +func (e *LockoutPolicyChangedEvent) Payload() interface{} { return e } -func (e *LockoutPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LockoutPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -101,12 +97,12 @@ func ChangeShowLockOutFailures(showLockOutFailures bool) func(*LockoutPolicyChan } } -func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &LockoutPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-lWGRc", "unable to unmarshal policy") } @@ -118,11 +114,11 @@ type LockoutPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *LockoutPolicyRemovedEvent) Data() interface{} { +func (e *LockoutPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *LockoutPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *LockoutPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -132,7 +128,7 @@ func NewLockoutPolicyRemovedEvent(base *eventstore.BaseEvent) *LockoutPolicyRemo } } -func LockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func LockoutPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &LockoutPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/policy/policy_privacy.go b/internal/repository/policy/policy_privacy.go index a168a91cf..75782c3cd 100644 --- a/internal/repository/policy/policy_privacy.go +++ b/internal/repository/policy/policy_privacy.go @@ -1,12 +1,9 @@ package policy import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -24,11 +21,11 @@ type PrivacyPolicyAddedEvent struct { SupportEmail domain.EmailAddress `json:"supportEmail,omitempty"` } -func (e *PrivacyPolicyAddedEvent) Data() interface{} { +func (e *PrivacyPolicyAddedEvent) Payload() interface{} { return e } -func (e *PrivacyPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PrivacyPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -48,11 +45,11 @@ func NewPrivacyPolicyAddedEvent( } } -func PrivacyPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PrivacyPolicyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-2k0fs", "unable to unmarshal policy") } @@ -69,11 +66,11 @@ type PrivacyPolicyChangedEvent struct { SupportEmail *domain.EmailAddress `json:"supportEmail,omitempty"` } -func (e *PrivacyPolicyChangedEvent) Data() interface{} { +func (e *PrivacyPolicyChangedEvent) Payload() interface{} { return e } -func (e *PrivacyPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PrivacyPolicyChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -119,12 +116,12 @@ func ChangeSupportEmail(supportEmail domain.EmailAddress) func(*PrivacyPolicyCha } } -func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &PrivacyPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-22nf9", "unable to unmarshal policy") } @@ -136,11 +133,11 @@ type PrivacyPolicyRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *PrivacyPolicyRemovedEvent) Data() interface{} { +func (e *PrivacyPolicyRemovedEvent) Payload() interface{} { return nil } -func (e *PrivacyPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PrivacyPolicyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -150,7 +147,7 @@ func NewPrivacyPolicyRemovedEvent(base *eventstore.BaseEvent) *PrivacyPolicyRemo } } -func PrivacyPolicyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PrivacyPolicyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &PrivacyPolicyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/project/api_config.go b/internal/repository/project/api_config.go index 1eeff4896..4f24589ac 100644 --- a/internal/repository/project/api_config.go +++ b/internal/repository/project/api_config.go @@ -2,14 +2,11 @@ package project import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -29,11 +26,11 @@ type APIConfigAddedEvent struct { AuthMethodType domain.APIAuthMethodType `json:"authMethodType,omitempty"` } -func (e *APIConfigAddedEvent) Data() interface{} { +func (e *APIConfigAddedEvent) Payload() interface{} { return e } -func (e *APIConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *APIConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -77,12 +74,12 @@ func (e *APIConfigAddedEvent) Validate(cmd eventstore.Command) bool { return true } -func APIConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func APIConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &APIConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-BFd15", "unable to unmarshal api config") } @@ -98,11 +95,11 @@ type APIConfigChangedEvent struct { AuthMethodType *domain.APIAuthMethodType `json:"authMethodType,omitempty"` } -func (e *APIConfigChangedEvent) Data() interface{} { +func (e *APIConfigChangedEvent) Payload() interface{} { return e } -func (e *APIConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *APIConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -138,12 +135,12 @@ func ChangeAPIAuthMethodType(authMethodType domain.APIAuthMethodType) func(event } } -func APIConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func APIConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &APIConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-BFd15", "unable to unmarshal api config") } @@ -158,11 +155,11 @@ type APIConfigSecretChangedEvent struct { ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` } -func (e *APIConfigSecretChangedEvent) Data() interface{} { +func (e *APIConfigSecretChangedEvent) Payload() interface{} { return e } -func (e *APIConfigSecretChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *APIConfigSecretChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -183,12 +180,12 @@ func NewAPIConfigSecretChangedEvent( } } -func APIConfigSecretChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func APIConfigSecretChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &APIConfigSecretChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-M893d", "unable to unmarshal api config") } @@ -202,11 +199,11 @@ type APIConfigSecretCheckSucceededEvent struct { AppID string `json:"appId"` } -func (e *APIConfigSecretCheckSucceededEvent) Data() interface{} { +func (e *APIConfigSecretCheckSucceededEvent) Payload() interface{} { return e } -func (e *APIConfigSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *APIConfigSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -225,12 +222,12 @@ func NewAPIConfigSecretCheckSucceededEvent( } } -func APIConfigSecretCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func APIConfigSecretCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &APIConfigSecretCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-837gV", "unable to unmarshal api config") } @@ -244,11 +241,11 @@ type APIConfigSecretCheckFailedEvent struct { AppID string `json:"appId"` } -func (e *APIConfigSecretCheckFailedEvent) Data() interface{} { +func (e *APIConfigSecretCheckFailedEvent) Payload() interface{} { return e } -func (e *APIConfigSecretCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *APIConfigSecretCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -267,12 +264,12 @@ func NewAPIConfigSecretCheckFailedEvent( } } -func APIConfigSecretCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func APIConfigSecretCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &APIConfigSecretCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-987g%", "unable to unmarshal api config") } diff --git a/internal/repository/project/application.go b/internal/repository/project/application.go index b2afbb9e0..be2a944ef 100644 --- a/internal/repository/project/application.go +++ b/internal/repository/project/application.go @@ -2,13 +2,10 @@ package project import ( "context" - "encoding/json" "fmt" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -21,15 +18,15 @@ const ( ApplicationRemovedType = applicationEventTypePrefix + "removed" ) -func NewAddApplicationUniqueConstraint(name, projectID string) *eventstore.EventUniqueConstraint { +func NewAddApplicationUniqueConstraint(name, projectID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueAppNameType, fmt.Sprintf("%s:%s", name, projectID), "Errors.Project.App.AlreadyExists") } -func NewRemoveApplicationUniqueConstraint(name, projectID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveApplicationUniqueConstraint(name, projectID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueAppNameType, fmt.Sprintf("%s:%s", name, projectID)) } @@ -41,12 +38,12 @@ type ApplicationAddedEvent struct { Name string `json:"name,omitempty"` } -func (e *ApplicationAddedEvent) Data() interface{} { +func (e *ApplicationAddedEvent) Payload() interface{} { return e } -func (e *ApplicationAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddApplicationUniqueConstraint(e.Name, e.Aggregate().ID)} +func (e *ApplicationAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddApplicationUniqueConstraint(e.Name, e.Aggregate().ID)} } func NewApplicationAddedEvent( @@ -66,12 +63,12 @@ func NewApplicationAddedEvent( } } -func ApplicationAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "APPLICATION-Nffg2", "unable to unmarshal application") } @@ -87,12 +84,12 @@ type ApplicationChangedEvent struct { oldName string } -func (e *ApplicationChangedEvent) Data() interface{} { +func (e *ApplicationChangedEvent) Payload() interface{} { return e } -func (e *ApplicationChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ +func (e *ApplicationChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ NewRemoveApplicationUniqueConstraint(e.oldName, e.Aggregate().ID), NewAddApplicationUniqueConstraint(e.Name, e.Aggregate().ID), } @@ -117,12 +114,12 @@ func NewApplicationChangedEvent( } } -func ApplicationChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "APPLICATION-9l0cs", "unable to unmarshal application") } @@ -136,11 +133,11 @@ type ApplicationDeactivatedEvent struct { AppID string `json:"appId,omitempty"` } -func (e *ApplicationDeactivatedEvent) Data() interface{} { +func (e *ApplicationDeactivatedEvent) Payload() interface{} { return e } -func (e *ApplicationDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ApplicationDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -159,12 +156,12 @@ func NewApplicationDeactivatedEvent( } } -func ApplicationDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "APPLICATION-0p9fB", "unable to unmarshal application") } @@ -178,11 +175,11 @@ type ApplicationReactivatedEvent struct { AppID string `json:"appId,omitempty"` } -func (e *ApplicationReactivatedEvent) Data() interface{} { +func (e *ApplicationReactivatedEvent) Payload() interface{} { return e } -func (e *ApplicationReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ApplicationReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -201,12 +198,12 @@ func NewApplicationReactivatedEvent( } } -func ApplicationReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "APPLICATION-1m9e3", "unable to unmarshal application") } @@ -222,12 +219,12 @@ type ApplicationRemovedEvent struct { entityID string } -func (e *ApplicationRemovedEvent) Data() interface{} { +func (e *ApplicationRemovedEvent) Payload() interface{} { return e } -func (e *ApplicationRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - remove := []*eventstore.EventUniqueConstraint{NewRemoveApplicationUniqueConstraint(e.name, e.Aggregate().ID)} +func (e *ApplicationRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + remove := []*eventstore.UniqueConstraint{NewRemoveApplicationUniqueConstraint(e.name, e.Aggregate().ID)} if e.entityID != "" { remove = append(remove, NewRemoveSAMLConfigEntityIDUniqueConstraint(e.entityID)) } @@ -253,12 +250,12 @@ func NewApplicationRemovedEvent( } } -func ApplicationRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "APPLICATION-1m9e3", "unable to unmarshal application") } diff --git a/internal/repository/project/grant.go b/internal/repository/project/grant.go index b1248ea12..e9c60b4da 100644 --- a/internal/repository/project/grant.go +++ b/internal/repository/project/grant.go @@ -2,13 +2,10 @@ package project import ( "context" - "encoding/json" "fmt" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) var ( @@ -22,15 +19,15 @@ var ( GrantRemovedType = grantEventTypePrefix + "removed" ) -func NewAddProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.EventUniqueConstraint { +func NewAddProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueGrantType, fmt.Sprintf("%s:%s", grantedOrgID, projectID), "Errors.Project.Grant.AlreadyExists") } -func NewRemoveProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueGrantType, fmt.Sprintf("%s:%s", grantedOrgID, projectID)) } @@ -43,12 +40,12 @@ type GrantAddedEvent struct { RoleKeys []string `json:"roleKeys,omitempty"` } -func (e *GrantAddedEvent) Data() interface{} { +func (e *GrantAddedEvent) Payload() interface{} { return e } -func (e *GrantAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.Aggregate().ID)} +func (e *GrantAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.Aggregate().ID)} } func NewGrantAddedEvent( @@ -70,12 +67,12 @@ func NewGrantAddedEvent( } } -func GrantAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-mL0vs", "unable to unmarshal project grant") } @@ -90,11 +87,11 @@ type GrantChangedEvent struct { RoleKeys []string `json:"roleKeys,omitempty"` } -func (e *GrantChangedEvent) Data() interface{} { +func (e *GrantChangedEvent) Payload() interface{} { return e } -func (e *GrantChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GrantChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -115,12 +112,12 @@ func NewGrantChangedEvent( } } -func GrantChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-mL0vs", "unable to unmarshal project grant") } @@ -135,11 +132,11 @@ type GrantCascadeChangedEvent struct { RoleKeys []string `json:"roleKeys,omitempty"` } -func (e *GrantCascadeChangedEvent) Data() interface{} { +func (e *GrantCascadeChangedEvent) Payload() interface{} { return e } -func (e *GrantCascadeChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GrantCascadeChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -160,12 +157,12 @@ func NewGrantCascadeChangedEvent( } } -func GrantCascadeChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantCascadeChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantCascadeChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-9o0se", "unable to unmarshal project grant") } @@ -179,11 +176,11 @@ type GrantDeactivateEvent struct { GrantID string `json:"grantId,omitempty"` } -func (e *GrantDeactivateEvent) Data() interface{} { +func (e *GrantDeactivateEvent) Payload() interface{} { return e } -func (e *GrantDeactivateEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GrantDeactivateEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -202,12 +199,12 @@ func NewGrantDeactivateEvent( } } -func GrantDeactivateEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantDeactivateEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantDeactivateEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-9o0se", "unable to unmarshal project grant") } @@ -221,11 +218,11 @@ type GrantReactivatedEvent struct { GrantID string `json:"grantId,omitempty"` } -func (e *GrantReactivatedEvent) Data() interface{} { +func (e *GrantReactivatedEvent) Payload() interface{} { return e } -func (e *GrantReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GrantReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -244,12 +241,12 @@ func NewGrantReactivatedEvent( } } -func GrantReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-78f7D", "unable to unmarshal project grant") } @@ -264,12 +261,12 @@ type GrantRemovedEvent struct { grantedOrgID string } -func (e *GrantRemovedEvent) Data() interface{} { +func (e *GrantRemovedEvent) Payload() interface{} { return e } -func (e *GrantRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.Aggregate().ID)} +func (e *GrantRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.Aggregate().ID)} } func NewGrantRemovedEvent( @@ -289,12 +286,12 @@ func NewGrantRemovedEvent( } } -func GrantRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-28jM8", "unable to unmarshal project grant") } diff --git a/internal/repository/project/grant_member.go b/internal/repository/project/grant_member.go index 2c0f41041..5e5e700d9 100644 --- a/internal/repository/project/grant_member.go +++ b/internal/repository/project/grant_member.go @@ -2,13 +2,10 @@ package project import ( "context" - "encoding/json" "fmt" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/member" ) @@ -20,15 +17,15 @@ var ( GrantMemberCascadeRemovedType = grantEventTypePrefix + member.CascadeRemovedEventType ) -func NewAddProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.EventUniqueConstraint { +func NewAddProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueProjectGrantMemberType, fmt.Sprintf("%s:%s:%s", projectID, userID, grantID), "Errors.Project.Member.AlreadyExists") } -func NewRemoveProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueProjectGrantMemberType, fmt.Sprintf("%s:%s:%s", projectID, userID, grantID), ) @@ -42,12 +39,12 @@ type GrantMemberAddedEvent struct { GrantID string `json:"grantId"` } -func (e *GrantMemberAddedEvent) Data() interface{} { +func (e *GrantMemberAddedEvent) Payload() interface{} { return e } -func (e *GrantMemberAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} +func (e *GrantMemberAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} } func NewProjectGrantMemberAddedEvent( @@ -69,12 +66,12 @@ func NewProjectGrantMemberAddedEvent( } } -func GrantMemberAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantMemberAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantMemberAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-9f0sf", "unable to unmarshal label policy") } @@ -90,11 +87,11 @@ type GrantMemberChangedEvent struct { UserID string `json:"userId"` } -func (e *GrantMemberChangedEvent) Data() interface{} { +func (e *GrantMemberChangedEvent) Payload() interface{} { return e } -func (e *GrantMemberChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *GrantMemberChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -117,12 +114,12 @@ func NewProjectGrantMemberChangedEvent( } } -func GrantMemberChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantMemberChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantMemberChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-39fi8", "unable to unmarshal label policy") } @@ -137,12 +134,12 @@ type GrantMemberRemovedEvent struct { GrantID string `json:"grantId"` } -func (e *GrantMemberRemovedEvent) Data() interface{} { +func (e *GrantMemberRemovedEvent) Payload() interface{} { return e } -func (e *GrantMemberRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} +func (e *GrantMemberRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} } func NewProjectGrantMemberRemovedEvent( @@ -162,12 +159,12 @@ func NewProjectGrantMemberRemovedEvent( } } -func GrantMemberRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantMemberRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantMemberRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-173fM", "unable to unmarshal label policy") } @@ -182,12 +179,12 @@ type GrantMemberCascadeRemovedEvent struct { GrantID string `json:"grantId"` } -func (e *GrantMemberCascadeRemovedEvent) Data() interface{} { +func (e *GrantMemberCascadeRemovedEvent) Payload() interface{} { return e } -func (e *GrantMemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} +func (e *GrantMemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} } func NewProjectGrantMemberCascadeRemovedEvent( @@ -207,12 +204,12 @@ func NewProjectGrantMemberCascadeRemovedEvent( } } -func GrantMemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func GrantMemberCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &GrantMemberCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-3kfs3", "unable to unmarshal label policy") } diff --git a/internal/repository/project/key.go b/internal/repository/project/key.go index 0f2eb11a5..4eb03c8dd 100644 --- a/internal/repository/project/key.go +++ b/internal/repository/project/key.go @@ -2,14 +2,11 @@ package project import ( "context" - "encoding/json" "time" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -29,11 +26,11 @@ type ApplicationKeyAddedEvent struct { PublicKey []byte `json:"publicKey,omitempty"` } -func (e *ApplicationKeyAddedEvent) Data() interface{} { +func (e *ApplicationKeyAddedEvent) Payload() interface{} { return e } -func (e *ApplicationKeyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ApplicationKeyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -62,12 +59,12 @@ func NewApplicationKeyAddedEvent( } } -func ApplicationKeyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationKeyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ApplicationKeyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "API-BFd15", "unable to unmarshal api config") } @@ -81,11 +78,11 @@ type ApplicationKeyRemovedEvent struct { KeyID string `json:"keyId,omitempty"` } -func (e *ApplicationKeyRemovedEvent) Data() interface{} { +func (e *ApplicationKeyRemovedEvent) Payload() interface{} { return e } -func (e *ApplicationKeyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ApplicationKeyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -104,13 +101,13 @@ func NewApplicationKeyRemovedEvent( } } -func ApplicationKeyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ApplicationKeyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { applicationKeyRemoved := &ApplicationKeyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, applicationKeyRemoved) + err := event.Unmarshal(applicationKeyRemoved) if err != nil { - return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal application key removed") + return nil, errors.ThrowInternal(err, "USER-cjLeA", "unable to unmarshal application key removed") } return applicationKeyRemoved, nil diff --git a/internal/repository/project/member.go b/internal/repository/project/member.go index f32062d27..d2928bfdc 100644 --- a/internal/repository/project/member.go +++ b/internal/repository/project/member.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/member" ) @@ -39,7 +37,7 @@ func NewProjectMemberAddedEvent( } } -func MemberAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.MemberAddedEventMapper(event) if err != nil { return nil, err @@ -72,7 +70,7 @@ func NewProjectMemberChangedEvent( } } -func MemberChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.ChangedEventMapper(event) if err != nil { return nil, err @@ -103,7 +101,7 @@ func NewProjectMemberRemovedEvent( } } -func MemberRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.RemovedEventMapper(event) if err != nil { return nil, err @@ -134,7 +132,7 @@ func NewProjectMemberCascadeRemovedEvent( } } -func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MemberCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := member.CascadeRemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/project/oidc_config.go b/internal/repository/project/oidc_config.go index d39e92c2a..977af91ea 100644 --- a/internal/repository/project/oidc_config.go +++ b/internal/repository/project/oidc_config.go @@ -2,14 +2,12 @@ package project import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -43,11 +41,11 @@ type OIDCConfigAddedEvent struct { SkipNativeAppSuccessPage bool `json:"skipNativeAppSuccessPage,omitempty"` } -func (e *OIDCConfigAddedEvent) Data() interface{} { +func (e *OIDCConfigAddedEvent) Payload() interface{} { return e } -func (e *OIDCConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -185,12 +183,12 @@ func (e *OIDCConfigAddedEvent) Validate(cmd eventstore.Command) bool { return e.SkipNativeAppSuccessPage == c.SkipNativeAppSuccessPage } -func OIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-BFd15", "unable to unmarshal oidc config") } @@ -219,11 +217,11 @@ type OIDCConfigChangedEvent struct { SkipNativeAppSuccessPage *bool `json:"skipNativeAppSuccessPage,omitempty"` } -func (e *OIDCConfigChangedEvent) Data() interface{} { +func (e *OIDCConfigChangedEvent) Payload() interface{} { return e } -func (e *OIDCConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -343,12 +341,12 @@ func ChangeSkipNativeAppSuccessPage(skipNativeAppSuccessPage bool) func(event *O } } -func OIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-BFd15", "unable to unmarshal oidc config") } @@ -363,11 +361,11 @@ type OIDCConfigSecretChangedEvent struct { ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` } -func (e *OIDCConfigSecretChangedEvent) Data() interface{} { +func (e *OIDCConfigSecretChangedEvent) Payload() interface{} { return e } -func (e *OIDCConfigSecretChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigSecretChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -388,12 +386,12 @@ func NewOIDCConfigSecretChangedEvent( } } -func OIDCConfigSecretChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigSecretChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigSecretChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-M893d", "unable to unmarshal oidc config") } @@ -407,11 +405,11 @@ type OIDCConfigSecretCheckSucceededEvent struct { AppID string `json:"appId"` } -func (e *OIDCConfigSecretCheckSucceededEvent) Data() interface{} { +func (e *OIDCConfigSecretCheckSucceededEvent) Payload() interface{} { return e } -func (e *OIDCConfigSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -430,12 +428,12 @@ func NewOIDCConfigSecretCheckSucceededEvent( } } -func OIDCConfigSecretCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigSecretCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigSecretCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-837gV", "unable to unmarshal oidc config") } @@ -449,11 +447,11 @@ type OIDCConfigSecretCheckFailedEvent struct { AppID string `json:"appId"` } -func (e *OIDCConfigSecretCheckFailedEvent) Data() interface{} { +func (e *OIDCConfigSecretCheckFailedEvent) Payload() interface{} { return e } -func (e *OIDCConfigSecretCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OIDCConfigSecretCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -472,12 +470,12 @@ func NewOIDCConfigSecretCheckFailedEvent( } } -func OIDCConfigSecretCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func OIDCConfigSecretCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &OIDCConfigSecretCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "OIDC-987g%", "unable to unmarshal oidc config") } diff --git a/internal/repository/project/project.go b/internal/repository/project/project.go index eb2abba07..f37814243 100644 --- a/internal/repository/project/project.go +++ b/internal/repository/project/project.go @@ -2,13 +2,10 @@ package project import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -21,15 +18,15 @@ const ( ProjectRemovedType = projectEventTypePrefix + "removed" ) -func NewAddProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.EventUniqueConstraint { +func NewAddProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueProjectnameType, projectName+resourceOwner, "Errors.Project.AlreadyExists") } -func NewRemoveProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueProjectnameType, projectName+resourceOwner) } @@ -44,12 +41,12 @@ type ProjectAddedEvent struct { PrivateLabelingSetting domain.PrivateLabelingSetting `json:"privateLabelingSetting,omitempty"` } -func (e *ProjectAddedEvent) Data() interface{} { +func (e *ProjectAddedEvent) Payload() interface{} { return e } -func (e *ProjectAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddProjectNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} +func (e *ProjectAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddProjectNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} } func NewProjectAddedEvent( @@ -75,12 +72,12 @@ func NewProjectAddedEvent( } } -func ProjectAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ProjectAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-Bfg2f", "unable to unmarshal project") } @@ -99,13 +96,13 @@ type ProjectChangeEvent struct { oldName string } -func (e *ProjectChangeEvent) Data() interface{} { +func (e *ProjectChangeEvent) Payload() interface{} { return e } -func (e *ProjectChangeEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - if e.oldName != "" { - return []*eventstore.EventUniqueConstraint{ +func (e *ProjectChangeEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + if e.Name != nil { + return []*eventstore.UniqueConstraint{ NewRemoveProjectNameUniqueConstraint(e.oldName, e.Aggregate().ResourceOwner), NewAddProjectNameUniqueConstraint(*e.Name, e.Aggregate().ResourceOwner), } @@ -168,12 +165,12 @@ func ChangePrivateLabelingSetting(ChangePrivateLabelingSetting domain.PrivateLab } } -func ProjectChangeEventMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectChangeEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &ProjectChangeEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-M9osd", "unable to unmarshal project") } @@ -185,11 +182,11 @@ type ProjectDeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *ProjectDeactivatedEvent) Data() interface{} { +func (e *ProjectDeactivatedEvent) Payload() interface{} { return nil } -func (e *ProjectDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ProjectDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -203,7 +200,7 @@ func NewProjectDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func ProjectDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &ProjectDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -213,11 +210,11 @@ type ProjectReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *ProjectReactivatedEvent) Data() interface{} { +func (e *ProjectReactivatedEvent) Payload() interface{} { return nil } -func (e *ProjectReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *ProjectReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -231,7 +228,7 @@ func NewProjectReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func ProjectReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &ProjectReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -241,15 +238,15 @@ type ProjectRemovedEvent struct { eventstore.BaseEvent `json:"-"` Name string - entityIDUniqueContraints []*eventstore.EventUniqueConstraint + entityIDUniqueContraints []*eventstore.UniqueConstraint } -func (e *ProjectRemovedEvent) Data() interface{} { +func (e *ProjectRemovedEvent) Payload() interface{} { return nil } -func (e *ProjectRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - constraints := []*eventstore.EventUniqueConstraint{NewRemoveProjectNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} +func (e *ProjectRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + constraints := []*eventstore.UniqueConstraint{NewRemoveProjectNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} if e.entityIDUniqueContraints != nil { for _, constraint := range e.entityIDUniqueContraints { constraints = append(constraints, constraint) @@ -262,7 +259,7 @@ func NewProjectRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, name string, - entityIDUniqueContraints []*eventstore.EventUniqueConstraint, + entityIDUniqueContraints []*eventstore.UniqueConstraint, ) *ProjectRemovedEvent { return &ProjectRemovedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -275,7 +272,7 @@ func NewProjectRemovedEvent( } } -func ProjectRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func ProjectRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &ProjectRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/project/role.go b/internal/repository/project/role.go index bc04ee549..e5bf74d0e 100644 --- a/internal/repository/project/role.go +++ b/internal/repository/project/role.go @@ -2,13 +2,10 @@ package project import ( "context" - "encoding/json" "fmt" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) var ( @@ -19,15 +16,15 @@ var ( RoleRemovedType = roleEventTypePrefix + "removed" ) -func NewAddProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.EventUniqueConstraint { +func NewAddProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueRoleType, fmt.Sprintf("%s:%s", roleKey, projectID), "Errors.Project.Role.AlreadyExists") } -func NewRemoveProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueRoleType, fmt.Sprintf("%s:%s", roleKey, projectID)) } @@ -40,12 +37,12 @@ type RoleAddedEvent struct { Group string `json:"group,omitempty"` } -func (e *RoleAddedEvent) Data() interface{} { +func (e *RoleAddedEvent) Payload() interface{} { return e } -func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} +func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} } func NewRoleAddedEvent( @@ -67,12 +64,12 @@ func NewRoleAddedEvent( } } -func RoleAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RoleAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RoleAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-2M0xy", "unable to unmarshal project role") } @@ -88,11 +85,11 @@ type RoleChangedEvent struct { Group *string `json:"group,omitempty"` } -func (e *RoleChangedEvent) Data() interface{} { +func (e *RoleChangedEvent) Payload() interface{} { return e } -func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -138,12 +135,12 @@ func ChangeGroup(group string) func(event *RoleChangedEvent) { e.Group = &group } } -func RoleChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RoleChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RoleChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-3M0vx", "unable to unmarshal project role") } @@ -157,12 +154,12 @@ type RoleRemovedEvent struct { Key string `json:"key,omitempty"` } -func (e *RoleRemovedEvent) Data() interface{} { +func (e *RoleRemovedEvent) Payload() interface{} { return e } -func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} +func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} } func NewRoleRemovedEvent( @@ -179,12 +176,12 @@ func NewRoleRemovedEvent( } } -func RoleRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RoleRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RoleRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "PROJECT-1M0xs", "unable to unmarshal project role") } diff --git a/internal/repository/project/saml_config.go b/internal/repository/project/saml_config.go index 6fc38469e..340648443 100644 --- a/internal/repository/project/saml_config.go +++ b/internal/repository/project/saml_config.go @@ -2,11 +2,9 @@ package project import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -24,25 +22,25 @@ type SAMLConfigAddedEvent struct { MetadataURL string `json:"metadata_url,omitempty"` } -func (e *SAMLConfigAddedEvent) Data() interface{} { +func (e *SAMLConfigAddedEvent) Payload() interface{} { return e } -func NewAddSAMLConfigEntityIDUniqueConstraint(entityID string) *eventstore.EventUniqueConstraint { +func NewAddSAMLConfigEntityIDUniqueConstraint(entityID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueEntityIDType, entityID, "Errors.Project.App.SAMLEntityIDAlreadyExists") } -func NewRemoveSAMLConfigEntityIDUniqueConstraint(entityID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveSAMLConfigEntityIDUniqueConstraint(entityID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueEntityIDType, entityID) } -func (e *SAMLConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddSAMLConfigEntityIDUniqueConstraint(e.EntityID)} +func (e *SAMLConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddSAMLConfigEntityIDUniqueConstraint(e.EntityID)} } func NewSAMLConfigAddedEvent( @@ -66,12 +64,12 @@ func NewSAMLConfigAddedEvent( } } -func SAMLConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLConfigAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "SAML-BDd15", "unable to unmarshal saml config") } @@ -89,13 +87,13 @@ type SAMLConfigChangedEvent struct { oldEntityID string } -func (e *SAMLConfigChangedEvent) Data() interface{} { +func (e *SAMLConfigChangedEvent) Payload() interface{} { return e } -func (e *SAMLConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SAMLConfigChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { if e.EntityID != "" { - return []*eventstore.EventUniqueConstraint{ + return []*eventstore.UniqueConstraint{ NewRemoveSAMLConfigEntityIDUniqueConstraint(e.oldEntityID), NewAddSAMLConfigEntityIDUniqueConstraint(e.EntityID), } @@ -149,12 +147,12 @@ func ChangeEntityID(entityID string) func(event *SAMLConfigChangedEvent) { } } -func SAMLConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func SAMLConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SAMLConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "SAML-BFd15", "unable to unmarshal saml config") } diff --git a/internal/repository/quota/events.go b/internal/repository/quota/events.go index 06f41cdd0..b52226689 100644 --- a/internal/repository/quota/events.go +++ b/internal/repository/quota/events.go @@ -2,13 +2,11 @@ package quota import ( "context" - "encoding/json" "strconv" "time" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type Unit uint @@ -29,7 +27,7 @@ const ( ActionsAllRunsSeconds ) -func NewAddQuotaUnitUniqueConstraint(unit Unit) *eventstore.EventUniqueConstraint { +func NewAddQuotaUnitUniqueConstraint(unit Unit) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueQuotaNameType, strconv.FormatUint(uint64(unit), 10), @@ -37,8 +35,8 @@ func NewAddQuotaUnitUniqueConstraint(unit Unit) *eventstore.EventUniqueConstrain ) } -func NewRemoveQuotaNameUniqueConstraint(unit Unit) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveQuotaNameUniqueConstraint(unit Unit) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueQuotaNameType, strconv.FormatUint(uint64(unit), 10), ) @@ -62,11 +60,11 @@ type SetEventNotification struct { CallURL string `json:"callUrl"` } -func (e *SetEvent) Data() interface{} { +func (e *SetEvent) Payload() interface{} { return e } -func (e *SetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *SetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -117,12 +115,11 @@ func ChangeNotifications(notifications []*SetEventNotification) QuotaChange { } } -func SetEventMapper(event *repository.Event) (eventstore.Event, error) { +func SetEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &SetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "QUOTA-kmIpI", "unable to unmarshal quota set") } @@ -140,11 +137,11 @@ type NotificationDueEvent struct { Usage uint64 `json:"usage"` } -func (n *NotificationDueEvent) Data() interface{} { +func (n *NotificationDueEvent) Payload() interface{} { return n } -func (n *NotificationDueEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (n *NotificationDueEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -173,12 +170,12 @@ func NewNotificationDueEvent( } } -func NotificationDueEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotificationDueEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &NotificationDueEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "QUOTA-k56rT", "unable to unmarshal notification due") } @@ -197,11 +194,11 @@ type NotifiedEvent struct { DueEventID string `json:"dueEventID"` } -func (e *NotifiedEvent) Data() interface{} { +func (e *NotifiedEvent) Payload() interface{} { return e } -func (e *NotifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *NotifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -214,7 +211,7 @@ func NewNotifiedEvent( return &NotifiedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, - &aggregate, + aggregate, NotifiedEventType, ), ID: id, @@ -232,12 +229,12 @@ func NewNotifiedEvent( } } -func NotifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func NotifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &NotifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "QUOTA-4n8vs", "unable to unmarshal quota notified") } @@ -250,12 +247,12 @@ type RemovedEvent struct { Unit Unit `json:"unit"` } -func (e *RemovedEvent) Data() interface{} { +func (e *RemovedEvent) Payload() interface{} { return e } -func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveQuotaNameUniqueConstraint(e.Unit)} +func (e *RemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveQuotaNameUniqueConstraint(e.Unit)} } func NewRemovedEvent( @@ -273,12 +270,12 @@ func NewRemovedEvent( } } -func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func RemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "QUOTA-4bReE", "unable to unmarshal quota removed") } diff --git a/internal/repository/session/session.go b/internal/repository/session/session.go index 8f5785a52..d7b8bd76b 100644 --- a/internal/repository/session/session.go +++ b/internal/repository/session/session.go @@ -2,7 +2,6 @@ package session import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" @@ -10,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -37,11 +35,11 @@ type AddedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *AddedEvent) Data() interface{} { +func (e *AddedEvent) Payload() interface{} { return e } -func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -57,11 +55,12 @@ func NewAddedEvent(ctx context.Context, } } -func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func AddedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-DG4gn", "unable to unmarshal session added") } @@ -76,11 +75,11 @@ type UserCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *UserCheckedEvent) Data() interface{} { +func (e *UserCheckedEvent) Payload() interface{} { return e } -func (e *UserCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -101,11 +100,11 @@ func NewUserCheckedEvent( } } -func UserCheckedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserCheckedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &UserCheckedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-DSGn5", "unable to unmarshal user checked") } @@ -119,11 +118,11 @@ type PasswordCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *PasswordCheckedEvent) Data() interface{} { +func (e *PasswordCheckedEvent) Payload() interface{} { return e } -func (e *PasswordCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PasswordCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -142,11 +141,11 @@ func NewPasswordCheckedEvent( } } -func PasswordCheckedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PasswordCheckedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &PasswordCheckedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-DGt21", "unable to unmarshal password checked") } @@ -160,11 +159,11 @@ type IntentCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *IntentCheckedEvent) Data() interface{} { +func (e *IntentCheckedEvent) Payload() interface{} { return e } -func (e *IntentCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *IntentCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -183,11 +182,11 @@ func NewIntentCheckedEvent( } } -func IntentCheckedEventMapper(event *repository.Event) (eventstore.Event, error) { +func IntentCheckedEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &IntentCheckedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-DGt90", "unable to unmarshal intent checked") } @@ -204,11 +203,11 @@ type WebAuthNChallengedEvent struct { RPID string `json:"rpid,omitempty"` } -func (e *WebAuthNChallengedEvent) Data() interface{} { +func (e *WebAuthNChallengedEvent) Payload() interface{} { return e } -func (e *WebAuthNChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *WebAuthNChallengedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -244,11 +243,11 @@ type WebAuthNCheckedEvent struct { UserVerified bool `json:"userVerified,omitempty"` } -func (e *WebAuthNCheckedEvent) Data() interface{} { +func (e *WebAuthNCheckedEvent) Payload() interface{} { return e } -func (e *WebAuthNCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *WebAuthNCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -279,11 +278,11 @@ type TOTPCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *TOTPCheckedEvent) Data() interface{} { +func (e *TOTPCheckedEvent) Payload() interface{} { return e } -func (e *TOTPCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *TOTPCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -315,11 +314,11 @@ type OTPSMSChallengedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *OTPSMSChallengedEvent) Data() interface{} { +func (e *OTPSMSChallengedEvent) Payload() interface{} { return e } -func (e *OTPSMSChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPSMSChallengedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -355,11 +354,11 @@ type OTPSMSSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *OTPSMSSentEvent) Data() interface{} { +func (e *OTPSMSSentEvent) Payload() interface{} { return e } -func (e *OTPSMSSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPSMSSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -386,11 +385,11 @@ type OTPSMSCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *OTPSMSCheckedEvent) Data() interface{} { +func (e *OTPSMSCheckedEvent) Payload() interface{} { return e } -func (e *OTPSMSCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPSMSCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -423,11 +422,11 @@ type OTPEmailChallengedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *OTPEmailChallengedEvent) Data() interface{} { +func (e *OTPEmailChallengedEvent) Payload() interface{} { return e } -func (e *OTPEmailChallengedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPEmailChallengedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -465,11 +464,11 @@ type OTPEmailSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *OTPEmailSentEvent) Data() interface{} { +func (e *OTPEmailSentEvent) Payload() interface{} { return e } -func (e *OTPEmailSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPEmailSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -496,11 +495,11 @@ type OTPEmailCheckedEvent struct { CheckedAt time.Time `json:"checkedAt"` } -func (e *OTPEmailCheckedEvent) Data() interface{} { +func (e *OTPEmailCheckedEvent) Payload() interface{} { return e } -func (e *OTPEmailCheckedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *OTPEmailCheckedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -529,11 +528,11 @@ type TokenSetEvent struct { TokenID string `json:"tokenID"` } -func (e *TokenSetEvent) Data() interface{} { +func (e *TokenSetEvent) Payload() interface{} { return e } -func (e *TokenSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *TokenSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -552,11 +551,11 @@ func NewTokenSetEvent( } } -func TokenSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func TokenSetEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &TokenSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-Sf3va", "unable to unmarshal token set") } @@ -570,11 +569,11 @@ type MetadataSetEvent struct { Metadata map[string][]byte `json:"metadata"` } -func (e *MetadataSetEvent) Data() interface{} { +func (e *MetadataSetEvent) Payload() interface{} { return e } -func (e *MetadataSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MetadataSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -593,11 +592,11 @@ func NewMetadataSetEvent( } } -func MetadataSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataSetEventMapper(event eventstore.Event) (eventstore.Event, error) { added := &MetadataSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, added) + err := event.Unmarshal(added) if err != nil { return nil, errors.ThrowInternal(err, "SESSION-BD21d", "unable to unmarshal metadata set") } @@ -609,11 +608,11 @@ type TerminateEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *TerminateEvent) Data() interface{} { +func (e *TerminateEvent) Payload() interface{} { return e } -func (e *TerminateEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *TerminateEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -630,7 +629,7 @@ func NewTerminateEvent( } } -func TerminateEventMapper(event *repository.Event) (eventstore.Event, error) { +func TerminateEventMapper(event eventstore.Event) (eventstore.Event, error) { return &TerminateEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/settings/debug_notification.go b/internal/repository/settings/debug_notification.go index 30ceb49c3..c1ae68f87 100644 --- a/internal/repository/settings/debug_notification.go +++ b/internal/repository/settings/debug_notification.go @@ -1,11 +1,8 @@ package settings import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -23,11 +20,11 @@ type DebugNotificationProviderAddedEvent struct { Compact bool `json:"compact,omitempty"` } -func (e *DebugNotificationProviderAddedEvent) Data() interface{} { +func (e *DebugNotificationProviderAddedEvent) Payload() interface{} { return e } -func (e *DebugNotificationProviderAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DebugNotificationProviderAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -41,12 +38,12 @@ func NewDebugNotificationProviderAddedEvent( } } -func DebugNotificationProviderAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &DebugNotificationProviderAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "SET-f93ns", "unable to unmarshal debug notification added") } @@ -60,11 +57,11 @@ type DebugNotificationProviderChangedEvent struct { Compact *bool `json:"compact,omitempty"` } -func (e *DebugNotificationProviderChangedEvent) Data() interface{} { +func (e *DebugNotificationProviderChangedEvent) Payload() interface{} { return e } -func (e *DebugNotificationProviderChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DebugNotificationProviderChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -92,12 +89,12 @@ func ChangeCompact(compact bool) func(*DebugNotificationProviderChangedEvent) { } } -func DebugNotificationProviderChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &DebugNotificationProviderChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "POLIC-ehssl", "unable to unmarshal policy") } @@ -109,11 +106,11 @@ type DebugNotificationProviderRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *DebugNotificationProviderRemovedEvent) Data() interface{} { +func (e *DebugNotificationProviderRemovedEvent) Payload() interface{} { return nil } -func (e *DebugNotificationProviderRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DebugNotificationProviderRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -123,7 +120,7 @@ func NewDebugNotificationProviderRemovedEvent(base *eventstore.BaseEvent) *Debug } } -func DebugNotificationProviderRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DebugNotificationProviderRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &DebugNotificationProviderRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/user/human.go b/internal/repository/user/human.go index f314cd8df..7cbeaa9c7 100644 --- a/internal/repository/user/human.go +++ b/internal/repository/user/human.go @@ -2,7 +2,6 @@ package user import ( "context" - "encoding/json" "time" "golang.org/x/text/language" @@ -12,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -56,12 +54,12 @@ type HumanAddedEvent struct { ChangeRequired bool `json:"changeRequired,omitempty"` } -func (e *HumanAddedEvent) Data() interface{} { +func (e *HumanAddedEvent) Payload() interface{} { return e } -func (e *HumanAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} +func (e *HumanAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} } func (e *HumanAddedEvent) AddAddressData( @@ -124,13 +122,13 @@ func NewHumanAddedEvent( } } -func HumanAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { humanAdded := &HumanAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanAdded) + err := event.Unmarshal(humanAdded) if err != nil { - return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal human added") + return nil, errors.ThrowInternal(err, "USER-vGlhy", "unable to unmarshal human added") } return humanAdded, nil @@ -161,12 +159,12 @@ type HumanRegisteredEvent struct { ChangeRequired bool `json:"changeRequired,omitempty"` } -func (e *HumanRegisteredEvent) Data() interface{} { +func (e *HumanRegisteredEvent) Payload() interface{} { return e } -func (e *HumanRegisteredEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} +func (e *HumanRegisteredEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} } func (e *HumanRegisteredEvent) AddAddressData( @@ -229,11 +227,11 @@ func NewHumanRegisteredEvent( } } -func HumanRegisteredEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanRegisteredEventMapper(event eventstore.Event) (eventstore.Event, error) { humanRegistered := &HumanRegisteredEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanRegistered) + err := event.Unmarshal(humanRegistered) if err != nil { return nil, errors.ThrowInternal(err, "USER-3Vm9s", "unable to unmarshal human registered") } @@ -248,11 +246,11 @@ type HumanInitialCodeAddedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanInitialCodeAddedEvent) Data() interface{} { +func (e *HumanInitialCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanInitialCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanInitialCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -278,11 +276,11 @@ func NewHumanInitialCodeAddedEvent( } } -func HumanInitialCodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanInitialCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { humanRegistered := &HumanInitialCodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanRegistered) + err := event.Unmarshal(humanRegistered) if err != nil { return nil, errors.ThrowInternal(err, "USER-bM9se", "unable to unmarshal human initial code added") } @@ -294,11 +292,11 @@ type HumanInitialCodeSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanInitialCodeSentEvent) Data() interface{} { +func (e *HumanInitialCodeSentEvent) Payload() interface{} { return nil } -func (e *HumanInitialCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanInitialCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -312,7 +310,7 @@ func NewHumanInitialCodeSentEvent(ctx context.Context, aggregate *eventstore.Agg } } -func HumanInitialCodeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanInitialCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanInitialCodeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -322,11 +320,11 @@ type HumanInitializedCheckSucceededEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanInitializedCheckSucceededEvent) Data() interface{} { +func (e *HumanInitializedCheckSucceededEvent) Payload() interface{} { return nil } -func (e *HumanInitializedCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanInitializedCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -340,7 +338,7 @@ func NewHumanInitializedCheckSucceededEvent(ctx context.Context, aggregate *even } } -func HumanInitializedCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanInitializedCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanInitializedCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -350,11 +348,11 @@ type HumanInitializedCheckFailedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanInitializedCheckFailedEvent) Data() interface{} { +func (e *HumanInitializedCheckFailedEvent) Payload() interface{} { return nil } -func (e *HumanInitializedCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanInitializedCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -368,7 +366,7 @@ func NewHumanInitializedCheckFailedEvent(ctx context.Context, aggregate *eventst } } -func HumanInitializedCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanInitializedCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanInitializedCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -380,11 +378,11 @@ type HumanSignedOutEvent struct { UserAgentID string `json:"userAgentID"` } -func (e *HumanSignedOutEvent) Data() interface{} { +func (e *HumanSignedOutEvent) Payload() interface{} { return e } -func (e *HumanSignedOutEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanSignedOutEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -403,11 +401,11 @@ func NewHumanSignedOutEvent( } } -func HumanSignedOutEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanSignedOutEventMapper(event eventstore.Event) (eventstore.Event, error) { signedOut := &HumanSignedOutEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, signedOut) + err := event.Unmarshal(signedOut) if err != nil { return nil, errors.ThrowInternal(err, "USER-WFS3g", "unable to unmarshal human signed out") } diff --git a/internal/repository/user/human_address.go b/internal/repository/user/human_address.go index 70d888917..7681866f2 100644 --- a/internal/repository/user/human_address.go +++ b/internal/repository/user/human_address.go @@ -2,12 +2,9 @@ package user import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -25,11 +22,11 @@ type HumanAddressChangedEvent struct { StreetAddress *string `json:"streetAddress,omitempty"` } -func (e *HumanAddressChangedEvent) Data() interface{} { +func (e *HumanAddressChangedEvent) Payload() interface{} { return e } -func (e *HumanAddressChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanAddressChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -86,11 +83,11 @@ func ChangeStreetAddress(street string) func(event *HumanAddressChangedEvent) { } } -func HumanAddressChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanAddressChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { addressChanged := &HumanAddressChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, addressChanged) + err := event.Unmarshal(addressChanged) if err != nil { return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human address changed") } diff --git a/internal/repository/user/human_avatar.go b/internal/repository/user/human_avatar.go index 6b88b524a..cfea6573f 100644 --- a/internal/repository/user/human_avatar.go +++ b/internal/repository/user/human_avatar.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/asset" ) @@ -29,7 +28,7 @@ func NewHumanAvatarAddedEvent(ctx context.Context, aggregate *eventstore.Aggrega } } -func HumanAvatarAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanAvatarAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.AddedEventMapper(event) if err != nil { return nil, err @@ -53,7 +52,7 @@ func NewHumanAvatarRemovedEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanAvatarRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanAvatarRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := asset.RemovedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/user/human_email.go b/internal/repository/user/human_email.go index 51081dc6e..9706706c4 100644 --- a/internal/repository/user/human_email.go +++ b/internal/repository/user/human_email.go @@ -2,7 +2,6 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" @@ -10,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -29,11 +27,11 @@ type HumanEmailChangedEvent struct { EmailAddress domain.EmailAddress `json:"email,omitempty"` } -func (e *HumanEmailChangedEvent) Data() interface{} { +func (e *HumanEmailChangedEvent) Payload() interface{} { return e } -func (e *HumanEmailChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanEmailChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -48,11 +46,11 @@ func NewHumanEmailChangedEvent(ctx context.Context, aggregate *eventstore.Aggreg } } -func HumanEmailChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanEmailChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { emailChangedEvent := &HumanEmailChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, emailChangedEvent) + err := event.Unmarshal(emailChangedEvent) if err != nil { return nil, errors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed") } @@ -66,11 +64,11 @@ type HumanEmailVerifiedEvent struct { IsEmailVerified bool `json:"-"` } -func (e *HumanEmailVerifiedEvent) Data() interface{} { +func (e *HumanEmailVerifiedEvent) Payload() interface{} { return nil } -func (e *HumanEmailVerifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanEmailVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -84,7 +82,7 @@ func NewHumanEmailVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanEmailVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanEmailVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { emailVerified := &HumanEmailVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), IsEmailVerified: true, @@ -96,11 +94,11 @@ type HumanEmailVerificationFailedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanEmailVerificationFailedEvent) Data() interface{} { +func (e *HumanEmailVerificationFailedEvent) Payload() interface{} { return nil } -func (e *HumanEmailVerificationFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanEmailVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -114,7 +112,7 @@ func NewHumanEmailVerificationFailedEvent(ctx context.Context, aggregate *events } } -func HumanEmailVerificationFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanEmailVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanEmailVerificationFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -130,11 +128,11 @@ type HumanEmailCodeAddedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanEmailCodeAddedEvent) Data() interface{} { +func (e *HumanEmailCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanEmailCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanEmailCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -173,11 +171,11 @@ func NewHumanEmailCodeAddedEventV2( } } -func HumanEmailCodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanEmailCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { codeAdded := &HumanEmailCodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, codeAdded) + err := event.Unmarshal(codeAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-3M0sd", "unable to unmarshal human email code added") } @@ -189,11 +187,11 @@ type HumanEmailCodeSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanEmailCodeSentEvent) Data() interface{} { +func (e *HumanEmailCodeSentEvent) Payload() interface{} { return nil } -func (e *HumanEmailCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanEmailCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -207,7 +205,7 @@ func NewHumanEmailCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanEmailCodeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanEmailCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanEmailCodeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/user/human_external_idp.go b/internal/repository/user/human_external_idp.go index e5a62e00b..cf698f447 100644 --- a/internal/repository/user/human_external_idp.go +++ b/internal/repository/user/human_external_idp.go @@ -2,12 +2,9 @@ package user import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -23,15 +20,15 @@ const ( UserIDPLoginCheckSucceededType = idpLoginEventPrefix + "check.succeeded" ) -func NewAddUserIDPLinkUniqueConstraint(idpConfigID, externalUserID string) *eventstore.EventUniqueConstraint { +func NewAddUserIDPLinkUniqueConstraint(idpConfigID, externalUserID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueUserIDPLinkType, idpConfigID+externalUserID, "Errors.User.ExternalIDP.AlreadyExists") } -func NewRemoveUserIDPLinkUniqueConstraint(idpConfigID, externalUserID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveUserIDPLinkUniqueConstraint(idpConfigID, externalUserID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueUserIDPLinkType, idpConfigID+externalUserID) } @@ -44,12 +41,12 @@ type UserIDPLinkAddedEvent struct { DisplayName string `json:"displayName,omitempty"` } -func (e *UserIDPLinkAddedEvent) Data() interface{} { +func (e *UserIDPLinkAddedEvent) Payload() interface{} { return e } -func (e *UserIDPLinkAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} +func (e *UserIDPLinkAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} } func NewUserIDPLinkAddedEvent( @@ -71,12 +68,12 @@ func NewUserIDPLinkAddedEvent( } } -func UserIDPLinkAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserIDPLinkAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserIDPLinkAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "USER-6M9sd", "unable to unmarshal user external idp added") } @@ -91,12 +88,12 @@ type UserIDPLinkRemovedEvent struct { ExternalUserID string `json:"userId,omitempty"` } -func (e *UserIDPLinkRemovedEvent) Data() interface{} { +func (e *UserIDPLinkRemovedEvent) Payload() interface{} { return e } -func (e *UserIDPLinkRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} +func (e *UserIDPLinkRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} } func NewUserIDPLinkRemovedEvent( @@ -116,14 +113,14 @@ func NewUserIDPLinkRemovedEvent( } } -func UserIDPLinkRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserIDPLinkRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserIDPLinkRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { - return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal user external idp removed") + return nil, errors.ThrowInternal(err, "USER-eAWoT", "unable to unmarshal user external idp removed") } return e, nil @@ -136,12 +133,12 @@ type UserIDPLinkCascadeRemovedEvent struct { ExternalUserID string `json:"userId,omitempty"` } -func (e *UserIDPLinkCascadeRemovedEvent) Data() interface{} { +func (e *UserIDPLinkCascadeRemovedEvent) Payload() interface{} { return e } -func (e *UserIDPLinkCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} +func (e *UserIDPLinkCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveUserIDPLinkUniqueConstraint(e.IDPConfigID, e.ExternalUserID)} } func NewUserIDPLinkCascadeRemovedEvent( @@ -161,12 +158,12 @@ func NewUserIDPLinkCascadeRemovedEvent( } } -func UserIDPLinkCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserIDPLinkCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserIDPLinkCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "USER-dKGqO", "unable to unmarshal user external idp cascade removed") } @@ -179,11 +176,11 @@ type UserIDPCheckSucceededEvent struct { *AuthRequestInfo } -func (e *UserIDPCheckSucceededEvent) Data() interface{} { +func (e *UserIDPCheckSucceededEvent) Payload() interface{} { return e } -func (e *UserIDPCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserIDPCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -201,12 +198,12 @@ func NewUserIDPCheckSucceededEvent( } } -func UserIDPCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserIDPCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserIDPCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "USER-oikSS", "unable to unmarshal user external idp check succeeded") } @@ -221,11 +218,11 @@ type UserIDPExternalIDMigratedEvent struct { NewID string `json:"newId"` } -func (e *UserIDPExternalIDMigratedEvent) Data() interface{} { +func (e *UserIDPExternalIDMigratedEvent) Payload() interface{} { return e } -func (e *UserIDPExternalIDMigratedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserIDPExternalIDMigratedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/user/human_mfa_events.go b/internal/repository/user/human_mfa_events.go index f0cc13877..7a1050431 100644 --- a/internal/repository/user/human_mfa_events.go +++ b/internal/repository/user/human_mfa_events.go @@ -4,8 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -17,11 +15,11 @@ type HumanMFAInitSkippedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanMFAInitSkippedEvent) Data() interface{} { +func (e *HumanMFAInitSkippedEvent) Payload() interface{} { return e } -func (e *HumanMFAInitSkippedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanMFAInitSkippedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -35,7 +33,7 @@ func NewHumanMFAInitSkippedEvent(ctx context.Context, aggregate *eventstore.Aggr } } -func HumanMFAInitSkippedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanMFAInitSkippedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanMFAInitSkippedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/user/human_mfa_otp.go b/internal/repository/user/human_mfa_otp.go index d3dd2824c..95fd0c0bb 100644 --- a/internal/repository/user/human_mfa_otp.go +++ b/internal/repository/user/human_mfa_otp.go @@ -2,15 +2,12 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -42,11 +39,11 @@ type HumanOTPAddedEvent struct { Secret *crypto.CryptoValue `json:"otpSecret,omitempty"` } -func (e *HumanOTPAddedEvent) Data() interface{} { +func (e *HumanOTPAddedEvent) Payload() interface{} { return e } -func (e *HumanOTPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -65,11 +62,11 @@ func NewHumanOTPAddedEvent( } } -func HumanOTPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanOTPAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { otpAdded := &HumanOTPAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, otpAdded) + err := event.Unmarshal(otpAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp added") } @@ -81,11 +78,11 @@ type HumanOTPVerifiedEvent struct { UserAgentID string `json:"userAgentID,omitempty"` } -func (e *HumanOTPVerifiedEvent) Data() interface{} { +func (e *HumanOTPVerifiedEvent) Payload() interface{} { return e } -func (e *HumanOTPVerifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -104,7 +101,7 @@ func NewHumanOTPVerifiedEvent( } } -func HumanOTPVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanOTPVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanOTPVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -114,11 +111,11 @@ type HumanOTPRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanOTPRemovedEvent) Data() interface{} { +func (e *HumanOTPRemovedEvent) Payload() interface{} { return nil } -func (e *HumanOTPRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -135,7 +132,7 @@ func NewHumanOTPRemovedEvent( } } -func HumanOTPRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanOTPRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanOTPRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -146,11 +143,11 @@ type HumanOTPCheckSucceededEvent struct { *AuthRequestInfo } -func (e *HumanOTPCheckSucceededEvent) Data() interface{} { +func (e *HumanOTPCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanOTPCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -169,11 +166,11 @@ func NewHumanOTPCheckSucceededEvent( } } -func HumanOTPCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanOTPCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { otpAdded := &HumanOTPCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, otpAdded) + err := event.Unmarshal(otpAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp check succeeded") } @@ -185,11 +182,11 @@ type HumanOTPCheckFailedEvent struct { *AuthRequestInfo } -func (e *HumanOTPCheckFailedEvent) Data() interface{} { +func (e *HumanOTPCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanOTPCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -208,11 +205,11 @@ func NewHumanOTPCheckFailedEvent( } } -func HumanOTPCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanOTPCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { otpAdded := &HumanOTPCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, otpAdded) + err := event.Unmarshal(otpAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp check failed") } @@ -223,11 +220,11 @@ type HumanOTPSMSAddedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanOTPSMSAddedEvent) Data() interface{} { +func (e *HumanOTPSMSAddedEvent) Payload() interface{} { return nil } -func (e *HumanOTPSMSAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -252,11 +249,11 @@ type HumanOTPSMSRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanOTPSMSRemovedEvent) Data() interface{} { +func (e *HumanOTPSMSRemovedEvent) Payload() interface{} { return nil } -func (e *HumanOTPSMSRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -286,11 +283,11 @@ type HumanOTPSMSCodeAddedEvent struct { *AuthRequestInfo } -func (e *HumanOTPSMSCodeAddedEvent) Data() interface{} { +func (e *HumanOTPSMSCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanOTPSMSCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -330,11 +327,11 @@ type HumanOTPSMSCodeSentEvent struct { *AuthRequestInfo } -func (e *HumanOTPSMSCodeSentEvent) Data() interface{} { +func (e *HumanOTPSMSCodeSentEvent) Payload() interface{} { return e } -func (e *HumanOTPSMSCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -360,11 +357,11 @@ type HumanOTPSMSCheckSucceededEvent struct { *AuthRequestInfo } -func (e *HumanOTPSMSCheckSucceededEvent) Data() interface{} { +func (e *HumanOTPSMSCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanOTPSMSCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -392,11 +389,11 @@ type HumanOTPSMSCheckFailedEvent struct { *AuthRequestInfo } -func (e *HumanOTPSMSCheckFailedEvent) Data() interface{} { +func (e *HumanOTPSMSCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanOTPSMSCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPSMSCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -423,11 +420,11 @@ type HumanOTPEmailAddedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanOTPEmailAddedEvent) Data() interface{} { +func (e *HumanOTPEmailAddedEvent) Payload() interface{} { return nil } -func (e *HumanOTPEmailAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -452,11 +449,11 @@ type HumanOTPEmailRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanOTPEmailRemovedEvent) Data() interface{} { +func (e *HumanOTPEmailRemovedEvent) Payload() interface{} { return nil } -func (e *HumanOTPEmailRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -486,11 +483,11 @@ type HumanOTPEmailCodeAddedEvent struct { *AuthRequestInfo } -func (e *HumanOTPEmailCodeAddedEvent) Data() interface{} { +func (e *HumanOTPEmailCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanOTPEmailCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -530,11 +527,11 @@ type HumanOTPEmailCodeSentEvent struct { *AuthRequestInfo } -func (e *HumanOTPEmailCodeSentEvent) Data() interface{} { +func (e *HumanOTPEmailCodeSentEvent) Payload() interface{} { return e } -func (e *HumanOTPEmailCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -560,11 +557,11 @@ type HumanOTPEmailCheckSucceededEvent struct { *AuthRequestInfo } -func (e *HumanOTPEmailCheckSucceededEvent) Data() interface{} { +func (e *HumanOTPEmailCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanOTPEmailCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -592,11 +589,11 @@ type HumanOTPEmailCheckFailedEvent struct { *AuthRequestInfo } -func (e *HumanOTPEmailCheckFailedEvent) Data() interface{} { +func (e *HumanOTPEmailCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanOTPEmailCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanOTPEmailCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/user/human_mfa_passwordless.go b/internal/repository/user/human_mfa_passwordless.go index abdee0c3d..ec6342d2e 100644 --- a/internal/repository/user/human_mfa_passwordless.go +++ b/internal/repository/user/human_mfa_passwordless.go @@ -2,7 +2,6 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" @@ -10,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -56,7 +54,7 @@ func NewHumanPasswordlessAddedEvent( } } -func HumanPasswordlessAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNAddedEventMapper(event) if err != nil { return nil, err @@ -100,7 +98,7 @@ func NewHumanPasswordlessVerifiedEvent( } } -func HumanPasswordlessVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNVerifiedEventMapper(event) if err != nil { return nil, err @@ -132,7 +130,7 @@ func NewHumanPasswordlessSignCountChangedEvent( } } -func HumanPasswordlessSignCountChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessSignCountChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNSignCountChangedEventMapper(event) if err != nil { return nil, err @@ -168,7 +166,7 @@ func NewHumanPasswordlessRemovedEvent( } } -func HumanPasswordlessRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNRemovedEventMapper(event) if err != nil { return nil, err @@ -202,7 +200,7 @@ func NewHumanPasswordlessBeginLoginEvent( } } -func HumanPasswordlessBeginLoginEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessBeginLoginEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNBeginLoginEventMapper(event) if err != nil { return nil, err @@ -231,7 +229,7 @@ func NewHumanPasswordlessCheckSucceededEvent( } } -func HumanPasswordlessCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNCheckSucceededEventMapper(event) if err != nil { return nil, err @@ -260,7 +258,7 @@ func NewHumanPasswordlessCheckFailedEvent( } } -func HumanPasswordlessCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNCheckFailedEventMapper(event) if err != nil { return nil, err @@ -277,11 +275,11 @@ type HumanPasswordlessInitCodeAddedEvent struct { Expiry time.Duration `json:"expiry"` } -func (e *HumanPasswordlessInitCodeAddedEvent) Data() interface{} { +func (e *HumanPasswordlessInitCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanPasswordlessInitCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordlessInitCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -304,11 +302,11 @@ func NewHumanPasswordlessInitCodeAddedEvent( } } -func HumanPasswordlessInitCodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessInitCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanPasswordlessInitCodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-BDf32", "unable to unmarshal human passwordless code added") } @@ -326,11 +324,11 @@ type HumanPasswordlessInitCodeRequestedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanPasswordlessInitCodeRequestedEvent) Data() interface{} { +func (e *HumanPasswordlessInitCodeRequestedEvent) Payload() interface{} { return e } -func (e *HumanPasswordlessInitCodeRequestedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordlessInitCodeRequestedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -362,11 +360,11 @@ func NewHumanPasswordlessInitCodeRequestedEvent( } } -func HumanPasswordlessInitCodeRequestedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessInitCodeRequestedEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanPasswordlessInitCodeRequestedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-VGfg3", "unable to unmarshal human passwordless code delivery added") } @@ -379,11 +377,11 @@ type HumanPasswordlessInitCodeSentEvent struct { ID string `json:"id"` } -func (e *HumanPasswordlessInitCodeSentEvent) Data() interface{} { +func (e *HumanPasswordlessInitCodeSentEvent) Payload() interface{} { return e } -func (e *HumanPasswordlessInitCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordlessInitCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -402,11 +400,11 @@ func NewHumanPasswordlessInitCodeSentEvent( } } -func HumanPasswordlessInitCodeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessInitCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanPasswordlessInitCodeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code sent") } @@ -419,11 +417,11 @@ type HumanPasswordlessInitCodeCheckFailedEvent struct { ID string `json:"id"` } -func (e *HumanPasswordlessInitCodeCheckFailedEvent) Data() interface{} { +func (e *HumanPasswordlessInitCodeCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanPasswordlessInitCodeCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordlessInitCodeCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -442,11 +440,11 @@ func NewHumanPasswordlessInitCodeCheckFailedEvent( } } -func HumanPasswordlessInitCodeCodeCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessInitCodeCodeCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanPasswordlessInitCodeCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code check failed") } @@ -459,11 +457,11 @@ type HumanPasswordlessInitCodeCheckSucceededEvent struct { ID string `json:"id"` } -func (e *HumanPasswordlessInitCodeCheckSucceededEvent) Data() interface{} { +func (e *HumanPasswordlessInitCodeCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanPasswordlessInitCodeCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordlessInitCodeCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -482,11 +480,11 @@ func NewHumanPasswordlessInitCodeCheckSucceededEvent( } } -func HumanPasswordlessInitCodeCodeCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordlessInitCodeCodeCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanPasswordlessInitCodeCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Gtg4j", "unable to unmarshal human passwordless code check succeeded") } diff --git a/internal/repository/user/human_mfa_u2f.go b/internal/repository/user/human_mfa_u2f.go index 096026b87..289a9bb3d 100644 --- a/internal/repository/user/human_mfa_u2f.go +++ b/internal/repository/user/human_mfa_u2f.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -44,7 +43,7 @@ func NewHumanU2FAddedEvent( } } -func HumanU2FAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNAddedEventMapper(event) if err != nil { return nil, err @@ -88,7 +87,7 @@ func NewHumanU2FVerifiedEvent( } } -func HumanU2FVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNVerifiedEventMapper(event) if err != nil { return nil, err @@ -120,7 +119,7 @@ func NewHumanU2FSignCountChangedEvent( } } -func HumanU2FSignCountChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FSignCountChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNSignCountChangedEventMapper(event) if err != nil { return nil, err @@ -156,7 +155,7 @@ func NewHumanU2FRemovedEvent( } } -func HumanU2FRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNRemovedEventMapper(event) if err != nil { return nil, err @@ -185,7 +184,7 @@ func NewHumanU2FBeginLoginEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanU2FBeginLoginEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FBeginLoginEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNBeginLoginEventMapper(event) if err != nil { return nil, err @@ -214,7 +213,7 @@ func NewHumanU2FCheckSucceededEvent( } } -func HumanU2FCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNCheckSucceededEventMapper(event) if err != nil { return nil, err @@ -243,7 +242,7 @@ func NewHumanU2FCheckFailedEvent( } } -func HumanU2FCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanU2FCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := HumanWebAuthNCheckFailedEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/user/human_mfa_web_auth_n.go b/internal/repository/user/human_mfa_web_auth_n.go index 77f27fb64..742580362 100644 --- a/internal/repository/user/human_mfa_web_auth_n.go +++ b/internal/repository/user/human_mfa_web_auth_n.go @@ -1,12 +1,9 @@ package user import ( - "encoding/json" - "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) type HumanWebAuthNAddedEvent struct { @@ -17,11 +14,11 @@ type HumanWebAuthNAddedEvent struct { RPID string `json:"rpID,omitempty"` } -func (e *HumanWebAuthNAddedEvent) Data() interface{} { +func (e *HumanWebAuthNAddedEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -39,11 +36,11 @@ func NewHumanWebAuthNAddedEvent( } } -func HumanWebAuthNAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanWebAuthNAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-tB8sf", "unable to unmarshal human webAuthN added") } @@ -63,11 +60,11 @@ type HumanWebAuthNVerifiedEvent struct { UserAgentID string `json:"userAgentID,omitempty"` } -func (e *HumanWebAuthNVerifiedEvent) Data() interface{} { +func (e *HumanWebAuthNVerifiedEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNVerifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -95,11 +92,11 @@ func NewHumanWebAuthNVerifiedEvent( } } -func HumanWebAuthNVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { webauthNVerified := &HumanWebAuthNVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webauthNVerified) + err := event.Unmarshal(webauthNVerified) if err != nil { return nil, errors.ThrowInternal(err, "USER-B0zDs", "unable to unmarshal human webAuthN verified") } @@ -113,11 +110,11 @@ type HumanWebAuthNSignCountChangedEvent struct { SignCount uint32 `json:"signCount"` } -func (e *HumanWebAuthNSignCountChangedEvent) Data() interface{} { +func (e *HumanWebAuthNSignCountChangedEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNSignCountChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNSignCountChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -133,11 +130,11 @@ func NewHumanWebAuthNSignCountChangedEvent( } } -func HumanWebAuthNSignCountChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNSignCountChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { webauthNVerified := &HumanWebAuthNSignCountChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webauthNVerified) + err := event.Unmarshal(webauthNVerified) if err != nil { return nil, errors.ThrowInternal(err, "USER-5Gm0s", "unable to unmarshal human webAuthN sign count") } @@ -151,11 +148,11 @@ type HumanWebAuthNRemovedEvent struct { State domain.MFAState `json:"-"` } -func (e *HumanWebAuthNRemovedEvent) Data() interface{} { +func (e *HumanWebAuthNRemovedEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -169,11 +166,11 @@ func NewHumanWebAuthNRemovedEvent( } } -func HumanWebAuthNRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { webauthNVerified := &HumanWebAuthNRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webauthNVerified) + err := event.Unmarshal(webauthNVerified) if err != nil { return nil, errors.ThrowInternal(err, "USER-gM9sd", "unable to unmarshal human webAuthN token removed") } @@ -189,11 +186,11 @@ type HumanWebAuthNBeginLoginEvent struct { *AuthRequestInfo } -func (e *HumanWebAuthNBeginLoginEvent) Data() interface{} { +func (e *HumanWebAuthNBeginLoginEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNBeginLoginEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNBeginLoginEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -207,11 +204,11 @@ func NewHumanWebAuthNBeginLoginEvent(base *eventstore.BaseEvent, challenge strin } } -func HumanWebAuthNBeginLoginEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNBeginLoginEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanWebAuthNBeginLoginEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-rMb8x", "unable to unmarshal human webAuthN begin login") } @@ -223,11 +220,11 @@ type HumanWebAuthNCheckSucceededEvent struct { *AuthRequestInfo } -func (e *HumanWebAuthNCheckSucceededEvent) Data() interface{} { +func (e *HumanWebAuthNCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -240,11 +237,11 @@ func NewHumanWebAuthNCheckSucceededEvent( } } -func HumanWebAuthNCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanWebAuthNCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-2M0fg", "unable to unmarshal human webAuthN check succeeded") } @@ -256,11 +253,11 @@ type HumanWebAuthNCheckFailedEvent struct { *AuthRequestInfo } -func (e *HumanWebAuthNCheckFailedEvent) Data() interface{} { +func (e *HumanWebAuthNCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanWebAuthNCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanWebAuthNCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -273,11 +270,11 @@ func NewHumanWebAuthNCheckFailedEvent( } } -func HumanWebAuthNCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanWebAuthNCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { webAuthNAdded := &HumanWebAuthNCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, webAuthNAdded) + err := event.Unmarshal(webAuthNAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-O0dse", "unable to unmarshal human webAuthN check failed") } diff --git a/internal/repository/user/human_password.go b/internal/repository/user/human_password.go index 051914517..aaca42dfe 100644 --- a/internal/repository/user/human_password.go +++ b/internal/repository/user/human_password.go @@ -2,7 +2,6 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" @@ -10,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -36,11 +34,11 @@ type HumanPasswordChangedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanPasswordChangedEvent) Data() interface{} { +func (e *HumanPasswordChangedEvent) Payload() interface{} { return e } -func (e *HumanPasswordChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -68,11 +66,11 @@ func NewHumanPasswordChangedEvent( } } -func HumanPasswordChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { humanAdded := &HumanPasswordChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanAdded) + err := event.Unmarshal(humanAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed") } @@ -91,11 +89,11 @@ type HumanPasswordCodeAddedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanPasswordCodeAddedEvent) Data() interface{} { +func (e *HumanPasswordCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanPasswordCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -137,11 +135,11 @@ func NewHumanPasswordCodeAddedEventV2( } } -func HumanPasswordCodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { humanAdded := &HumanPasswordCodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanAdded) + err := event.Unmarshal(humanAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Ms90d", "unable to unmarshal human password code added") } @@ -153,11 +151,11 @@ type HumanPasswordCodeSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanPasswordCodeSentEvent) Data() interface{} { +func (e *HumanPasswordCodeSentEvent) Payload() interface{} { return nil } -func (e *HumanPasswordCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -171,7 +169,7 @@ func NewHumanPasswordCodeSentEvent(ctx context.Context, aggregate *eventstore.Ag } } -func HumanPasswordCodeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPasswordCodeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -181,11 +179,11 @@ type HumanPasswordChangeSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanPasswordChangeSentEvent) Data() interface{} { +func (e *HumanPasswordChangeSentEvent) Payload() interface{} { return nil } -func (e *HumanPasswordChangeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordChangeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -199,7 +197,7 @@ func NewHumanPasswordChangeSentEvent(ctx context.Context, aggregate *eventstore. } } -func HumanPasswordChangeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordChangeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPasswordChangeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -210,11 +208,11 @@ type HumanPasswordCheckSucceededEvent struct { *AuthRequestInfo } -func (e *HumanPasswordCheckSucceededEvent) Data() interface{} { +func (e *HumanPasswordCheckSucceededEvent) Payload() interface{} { return e } -func (e *HumanPasswordCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -233,11 +231,11 @@ func NewHumanPasswordCheckSucceededEvent( } } -func HumanPasswordCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { humanAdded := &HumanPasswordCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanAdded) + err := event.Unmarshal(humanAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-5M9sd", "unable to unmarshal human password check succeeded") } @@ -250,11 +248,11 @@ type HumanPasswordCheckFailedEvent struct { *AuthRequestInfo } -func (e *HumanPasswordCheckFailedEvent) Data() interface{} { +func (e *HumanPasswordCheckFailedEvent) Payload() interface{} { return e } -func (e *HumanPasswordCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -273,11 +271,11 @@ func NewHumanPasswordCheckFailedEvent( } } -func HumanPasswordCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPasswordCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { humanAdded := &HumanPasswordCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, humanAdded) + err := event.Unmarshal(humanAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-4m9fs", "unable to unmarshal human password check failed") } @@ -290,11 +288,11 @@ type HumanPasswordHashUpdatedEvent struct { EncodedHash string `json:"encodedHash,omitempty"` } -func (e *HumanPasswordHashUpdatedEvent) Data() interface{} { +func (e *HumanPasswordHashUpdatedEvent) Payload() interface{} { return e } -func (e *HumanPasswordHashUpdatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPasswordHashUpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } diff --git a/internal/repository/user/human_phone.go b/internal/repository/user/human_phone.go index 135b8f899..7c2302bb4 100644 --- a/internal/repository/user/human_phone.go +++ b/internal/repository/user/human_phone.go @@ -2,7 +2,6 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" @@ -10,7 +9,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -29,11 +27,11 @@ type HumanPhoneChangedEvent struct { PhoneNumber domain.PhoneNumber `json:"phone,omitempty"` } -func (e *HumanPhoneChangedEvent) Data() interface{} { +func (e *HumanPhoneChangedEvent) Payload() interface{} { return e } -func (e *HumanPhoneChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -48,11 +46,11 @@ func NewHumanPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggreg } } -func HumanPhoneChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { phoneChangedEvent := &HumanPhoneChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, phoneChangedEvent) + err := event.Unmarshal(phoneChangedEvent) if err != nil { return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human phone changed") } @@ -64,11 +62,11 @@ type HumanPhoneRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanPhoneRemovedEvent) Data() interface{} { +func (e *HumanPhoneRemovedEvent) Payload() interface{} { return nil } -func (e *HumanPhoneRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -82,7 +80,7 @@ func NewHumanPhoneRemovedEvent(ctx context.Context, aggregate *eventstore.Aggreg } } -func HumanPhoneRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPhoneRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -94,11 +92,11 @@ type HumanPhoneVerifiedEvent struct { IsPhoneVerified bool `json:"-"` } -func (e *HumanPhoneVerifiedEvent) Data() interface{} { +func (e *HumanPhoneVerifiedEvent) Payload() interface{} { return nil } -func (e *HumanPhoneVerifiedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -112,7 +110,7 @@ func NewHumanPhoneVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanPhoneVerifiedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPhoneVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), IsPhoneVerified: true, @@ -123,11 +121,11 @@ type HumanPhoneVerificationFailedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanPhoneVerificationFailedEvent) Data() interface{} { +func (e *HumanPhoneVerificationFailedEvent) Payload() interface{} { return nil } -func (e *HumanPhoneVerificationFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -141,7 +139,7 @@ func NewHumanPhoneVerificationFailedEvent(ctx context.Context, aggregate *events } } -func HumanPhoneVerificationFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPhoneVerificationFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -156,11 +154,11 @@ type HumanPhoneCodeAddedEvent struct { TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` } -func (e *HumanPhoneCodeAddedEvent) Data() interface{} { +func (e *HumanPhoneCodeAddedEvent) Payload() interface{} { return e } -func (e *HumanPhoneCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -196,11 +194,11 @@ func NewHumanPhoneCodeAddedEventV2( } } -func HumanPhoneCodeAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { codeAdded := &HumanPhoneCodeAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, codeAdded) + err := event.Unmarshal(codeAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-6Ms9d", "unable to unmarshal human phone code added") } @@ -212,11 +210,11 @@ type HumanPhoneCodeSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *HumanPhoneCodeSentEvent) Data() interface{} { +func (e *HumanPhoneCodeSentEvent) Payload() interface{} { return e } -func (e *HumanPhoneCodeSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanPhoneCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -230,7 +228,7 @@ func NewHumanPhoneCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func HumanPhoneCodeSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanPhoneCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &HumanPhoneCodeSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/repository/user/human_profile.go b/internal/repository/user/human_profile.go index 24aeeab72..611591dbf 100644 --- a/internal/repository/user/human_profile.go +++ b/internal/repository/user/human_profile.go @@ -2,13 +2,10 @@ package user import ( "context" - "encoding/json" - - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" "golang.org/x/text/language" ) @@ -28,11 +25,11 @@ type HumanProfileChangedEvent struct { Gender *domain.Gender `json:"gender,omitempty"` } -func (e *HumanProfileChangedEvent) Data() interface{} { +func (e *HumanProfileChangedEvent) Payload() interface{} { return e } -func (e *HumanProfileChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanProfileChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -95,11 +92,11 @@ func ChangeGender(gender domain.Gender) func(event *HumanProfileChangedEvent) { } } -func HumanProfileChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanProfileChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { profileChanged := &HumanProfileChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, profileChanged) + err := event.Unmarshal(profileChanged) if err != nil { return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human profile changed") } diff --git a/internal/repository/user/human_refresh_token.go b/internal/repository/user/human_refresh_token.go index e2e9bd463..f17d906f3 100644 --- a/internal/repository/user/human_refresh_token.go +++ b/internal/repository/user/human_refresh_token.go @@ -2,13 +2,10 @@ package user import ( "context" - "encoding/json" "time" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -33,11 +30,11 @@ type HumanRefreshTokenAddedEvent struct { PreferredLanguage string `json:"preferredLanguage"` } -func (e *HumanRefreshTokenAddedEvent) Data() interface{} { +func (e *HumanRefreshTokenAddedEvent) Payload() interface{} { return e } -func (e *HumanRefreshTokenAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanRefreshTokenAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -78,11 +75,11 @@ func NewHumanRefreshTokenAddedEvent( } } -func HumanRefreshTokenAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanRefreshTokenAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { refreshTokenAdded := &HumanRefreshTokenAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, refreshTokenAdded) + err := event.Unmarshal(refreshTokenAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-DGr14", "unable to unmarshal refresh token added") } @@ -98,11 +95,11 @@ type HumanRefreshTokenRenewedEvent struct { IdleExpiration time.Duration `json:"idleExpiration"` } -func (e *HumanRefreshTokenRenewedEvent) Data() interface{} { +func (e *HumanRefreshTokenRenewedEvent) Payload() interface{} { return e } -func (e *HumanRefreshTokenRenewedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanRefreshTokenRenewedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -129,11 +126,11 @@ func NewHumanRefreshTokenRenewedEvent( } } -func HumanRefreshTokenRenewedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanRefreshTokenRenewedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenAdded := &HumanRefreshTokenRenewedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenAdded) + err := event.Unmarshal(tokenAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-GBt21", "unable to unmarshal refresh token renewed") } @@ -147,11 +144,11 @@ type HumanRefreshTokenRemovedEvent struct { TokenID string `json:"tokenId"` } -func (e *HumanRefreshTokenRemovedEvent) Data() interface{} { +func (e *HumanRefreshTokenRemovedEvent) Payload() interface{} { return e } -func (e *HumanRefreshTokenRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *HumanRefreshTokenRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -174,11 +171,11 @@ func NewHumanRefreshTokenRemovedEvent( } } -func HumanRefreshTokenRemovedEventEventMapper(event *repository.Event) (eventstore.Event, error) { +func HumanRefreshTokenRemovedEventEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenAdded := &HumanRefreshTokenRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenAdded) + err := event.Unmarshal(tokenAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Dggs2", "unable to unmarshal refresh token removed") } diff --git a/internal/repository/user/machine.go b/internal/repository/user/machine.go index 87cb57ed1..7faea5ac9 100644 --- a/internal/repository/user/machine.go +++ b/internal/repository/user/machine.go @@ -2,13 +2,10 @@ package user import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" + "github.com/zitadel/zitadel/internal/eventstore" ) const ( @@ -28,12 +25,12 @@ type MachineAddedEvent struct { AccessTokenType domain.OIDCTokenType `json:"accessTokenType,omitempty"` } -func (e *MachineAddedEvent) Data() interface{} { +func (e *MachineAddedEvent) Payload() interface{} { return e } -func (e *MachineAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} +func (e *MachineAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain)} } func NewMachineAddedEvent( @@ -59,11 +56,11 @@ func NewMachineAddedEvent( } } -func MachineAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { machineAdded := &MachineAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, machineAdded) + err := event.Unmarshal(machineAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-tMv9s", "unable to unmarshal machine added") } @@ -79,11 +76,11 @@ type MachineChangedEvent struct { AccessTokenType *domain.OIDCTokenType `json:"accessTokenType,omitempty"` } -func (e *MachineChangedEvent) Data() interface{} { +func (e *MachineChangedEvent) Payload() interface{} { return e } -func (e *MachineChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -128,11 +125,11 @@ func ChangeAccessTokenType(accessTokenType domain.OIDCTokenType) func(event *Mac } } -func MachineChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { machineChanged := &MachineChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, machineChanged) + err := event.Unmarshal(machineChanged) if err != nil { return nil, errors.ThrowInternal(err, "USER-4M9ds", "unable to unmarshal machine changed") } diff --git a/internal/repository/user/machine_key.go b/internal/repository/user/machine_key.go index 8c85f04c4..283e8240e 100644 --- a/internal/repository/user/machine_key.go +++ b/internal/repository/user/machine_key.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -26,11 +25,11 @@ type MachineKeyAddedEvent struct { PublicKey []byte `json:"publicKey,omitempty"` } -func (e *MachineKeyAddedEvent) Data() interface{} { +func (e *MachineKeyAddedEvent) Payload() interface{} { return e } -func (e *MachineKeyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineKeyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -55,11 +54,11 @@ func NewMachineKeyAddedEvent( } } -func MachineKeyAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineKeyAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { machineKeyAdded := &MachineKeyAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, machineKeyAdded) + err := event.Unmarshal(machineKeyAdded) if err != nil { //first events had wrong payload. // the keys were removed later, that's why we ignore them here. @@ -78,11 +77,11 @@ type MachineKeyRemovedEvent struct { KeyID string `json:"keyId,omitempty"` } -func (e *MachineKeyRemovedEvent) Data() interface{} { +func (e *MachineKeyRemovedEvent) Payload() interface{} { return e } -func (e *MachineKeyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineKeyRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -101,11 +100,11 @@ func NewMachineKeyRemovedEvent( } } -func MachineKeyRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineKeyRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { machineRemoved := &MachineKeyRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, machineRemoved) + err := event.Unmarshal(machineRemoved) if err != nil { return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal machine key removed") } diff --git a/internal/repository/user/machine_secret.go b/internal/repository/user/machine_secret.go index e21595b3c..041d938f8 100644 --- a/internal/repository/user/machine_secret.go +++ b/internal/repository/user/machine_secret.go @@ -2,12 +2,10 @@ package user import ( "context" - "encoding/json" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -24,11 +22,11 @@ type MachineSecretSetEvent struct { ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` } -func (e *MachineSecretSetEvent) Data() interface{} { +func (e *MachineSecretSetEvent) Payload() interface{} { return e } -func (e *MachineSecretSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineSecretSetEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -47,11 +45,11 @@ func NewMachineSecretSetEvent( } } -func MachineSecretSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineSecretSetEventMapper(event eventstore.Event) (eventstore.Event, error) { credentialsSet := &MachineSecretSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, credentialsSet) + err := event.Unmarshal(credentialsSet) if err != nil { return nil, errors.ThrowInternal(err, "USER-lopbqu", "unable to unmarshal machine secret set") } @@ -63,11 +61,11 @@ type MachineSecretRemovedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *MachineSecretRemovedEvent) Data() interface{} { +func (e *MachineSecretRemovedEvent) Payload() interface{} { return e } -func (e *MachineSecretRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineSecretRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -84,11 +82,11 @@ func NewMachineSecretRemovedEvent( } } -func MachineSecretRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineSecretRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { credentialsRemoved := &MachineSecretRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, credentialsRemoved) + err := event.Unmarshal(credentialsRemoved) if err != nil { return nil, errors.ThrowInternal(err, "USER-quox9j2", "unable to unmarshal machine secret removed") } @@ -100,11 +98,11 @@ type MachineSecretCheckSucceededEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *MachineSecretCheckSucceededEvent) Data() interface{} { +func (e *MachineSecretCheckSucceededEvent) Payload() interface{} { return e } -func (e *MachineSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -121,11 +119,11 @@ func NewMachineSecretCheckSucceededEvent( } } -func MachineSecretCheckSucceededEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineSecretCheckSucceededEventMapper(event eventstore.Event) (eventstore.Event, error) { check := &MachineSecretCheckSucceededEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, check) + err := event.Unmarshal(check) if err != nil { return nil, errors.ThrowInternal(err, "USER-x002n1p", "unable to unmarshal machine secret check succeeded") } @@ -137,11 +135,11 @@ type MachineSecretCheckFailedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *MachineSecretCheckFailedEvent) Data() interface{} { +func (e *MachineSecretCheckFailedEvent) Payload() interface{} { return e } -func (e *MachineSecretCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *MachineSecretCheckFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -158,11 +156,11 @@ func NewMachineSecretCheckFailedEvent( } } -func MachineSecretCheckFailedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MachineSecretCheckFailedEventMapper(event eventstore.Event) (eventstore.Event, error) { check := &MachineSecretCheckFailedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, check) + err := event.Unmarshal(check) if err != nil { return nil, errors.ThrowInternal(err, "USER-x7901b1l", "unable to unmarshal machine secret check failed") } diff --git a/internal/repository/user/metadata.go b/internal/repository/user/metadata.go index 64e621f54..cd4cd26b8 100644 --- a/internal/repository/user/metadata.go +++ b/internal/repository/user/metadata.go @@ -4,7 +4,6 @@ import ( "context" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/repository/metadata" ) @@ -30,7 +29,7 @@ func NewMetadataSetEvent(ctx context.Context, aggregate *eventstore.Aggregate, k } } -func MetadataSetEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataSetEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.SetEventMapper(event) if err != nil { return nil, err @@ -54,7 +53,7 @@ func NewMetadataRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func MetadataRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.RemovedEventMapper(event) if err != nil { return nil, err @@ -78,7 +77,7 @@ func NewMetadataRemovedAllEvent(ctx context.Context, aggregate *eventstore.Aggre } } -func MetadataRemovedAllEventMapper(event *repository.Event) (eventstore.Event, error) { +func MetadataRemovedAllEventMapper(event eventstore.Event) (eventstore.Event, error) { e, err := metadata.RemovedAllEventMapper(event) if err != nil { return nil, err diff --git a/internal/repository/user/personal_access_token.go b/internal/repository/user/personal_access_token.go index 3e7cfa570..10432fe22 100644 --- a/internal/repository/user/personal_access_token.go +++ b/internal/repository/user/personal_access_token.go @@ -2,12 +2,10 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -24,11 +22,11 @@ type PersonalAccessTokenAddedEvent struct { Scopes []string `json:"scopes"` } -func (e *PersonalAccessTokenAddedEvent) Data() interface{} { +func (e *PersonalAccessTokenAddedEvent) Payload() interface{} { return e } -func (e *PersonalAccessTokenAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PersonalAccessTokenAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -51,11 +49,11 @@ func NewPersonalAccessTokenAddedEvent( } } -func PersonalAccessTokenAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PersonalAccessTokenAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenAdded := &PersonalAccessTokenAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenAdded) + err := event.Unmarshal(tokenAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-Dbges", "unable to unmarshal token added") } @@ -69,11 +67,11 @@ type PersonalAccessTokenRemovedEvent struct { TokenID string `json:"tokenId"` } -func (e *PersonalAccessTokenRemovedEvent) Data() interface{} { +func (e *PersonalAccessTokenRemovedEvent) Payload() interface{} { return e } -func (e *PersonalAccessTokenRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *PersonalAccessTokenRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -92,11 +90,11 @@ func NewPersonalAccessTokenRemovedEvent( } } -func PersonalAccessTokenRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func PersonalAccessTokenRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenRemoved := &PersonalAccessTokenRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenRemoved) + err := event.Unmarshal(tokenRemoved) if err != nil { return nil, errors.ThrowInternal(err, "USER-Dbneg", "unable to unmarshal token removed") } diff --git a/internal/repository/user/user.go b/internal/repository/user/user.go index 1e6e7fbe6..df99eb2e7 100644 --- a/internal/repository/user/user.go +++ b/internal/repository/user/user.go @@ -2,14 +2,12 @@ package user import ( "context" - "encoding/json" "time" "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -27,7 +25,7 @@ const ( UserUserNameChangedType = userEventTypePrefix + "username.changed" ) -func NewAddUsernameUniqueConstraint(userName, resourceOwner string, userLoginMustBeDomain bool) *eventstore.EventUniqueConstraint { +func NewAddUsernameUniqueConstraint(userName, resourceOwner string, userLoginMustBeDomain bool) *eventstore.UniqueConstraint { uniqueUserName := userName if userLoginMustBeDomain { uniqueUserName = userName + resourceOwner @@ -38,12 +36,12 @@ func NewAddUsernameUniqueConstraint(userName, resourceOwner string, userLoginMus "Errors.User.AlreadyExists") } -func NewRemoveUsernameUniqueConstraint(userName, resourceOwner string, userLoginMustBeDomain bool) *eventstore.EventUniqueConstraint { +func NewRemoveUsernameUniqueConstraint(userName, resourceOwner string, userLoginMustBeDomain bool) *eventstore.UniqueConstraint { uniqueUserName := userName if userLoginMustBeDomain { uniqueUserName = userName + resourceOwner } - return eventstore.NewRemoveEventUniqueConstraint( + return eventstore.NewRemoveUniqueConstraint( UniqueUsername, uniqueUserName) } @@ -52,11 +50,11 @@ type UserLockedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserLockedEvent) Data() interface{} { +func (e *UserLockedEvent) Payload() interface{} { return nil } -func (e *UserLockedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserLockedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -70,7 +68,7 @@ func NewUserLockedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *U } } -func UserLockedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserLockedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserLockedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -80,11 +78,11 @@ type UserUnlockedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserUnlockedEvent) Data() interface{} { +func (e *UserUnlockedEvent) Payload() interface{} { return nil } -func (e *UserUnlockedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserUnlockedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -98,7 +96,7 @@ func NewUserUnlockedEvent(ctx context.Context, aggregate *eventstore.Aggregate) } } -func UserUnlockedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserUnlockedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserUnlockedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -108,11 +106,11 @@ type UserDeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserDeactivatedEvent) Data() interface{} { +func (e *UserDeactivatedEvent) Payload() interface{} { return nil } -func (e *UserDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -126,7 +124,7 @@ func NewUserDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func UserDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -136,11 +134,11 @@ type UserReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserReactivatedEvent) Data() interface{} { +func (e *UserReactivatedEvent) Payload() interface{} { return nil } -func (e *UserReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -154,7 +152,7 @@ func NewUserReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregat } } -func UserReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -168,12 +166,12 @@ type UserRemovedEvent struct { loginMustBeDomain bool } -func (e *UserRemovedEvent) Data() interface{} { +func (e *UserRemovedEvent) Payload() interface{} { return nil } -func (e *UserRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - events := make([]*eventstore.EventUniqueConstraint, 0) +func (e *UserRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + events := make([]*eventstore.UniqueConstraint, 0) if e.userName != "" { events = append(events, NewRemoveUsernameUniqueConstraint(e.userName, e.Aggregate().ResourceOwner, e.loginMustBeDomain)) } @@ -202,7 +200,7 @@ func NewUserRemovedEvent( } } -func UserRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -221,11 +219,11 @@ type UserTokenAddedEvent struct { PreferredLanguage string `json:"preferredLanguage"` } -func (e *UserTokenAddedEvent) Data() interface{} { +func (e *UserTokenAddedEvent) Payload() interface{} { return e } -func (e *UserTokenAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserTokenAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -258,11 +256,11 @@ func NewUserTokenAddedEvent( } } -func UserTokenAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserTokenAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenAdded := &UserTokenAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenAdded) + err := event.Unmarshal(tokenAdded) if err != nil { return nil, errors.ThrowInternal(err, "USER-7M9sd", "unable to unmarshal token added") } @@ -276,11 +274,11 @@ type UserTokenRemovedEvent struct { TokenID string `json:"tokenId"` } -func (e *UserTokenRemovedEvent) Data() interface{} { +func (e *UserTokenRemovedEvent) Payload() interface{} { return e } -func (e *UserTokenRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserTokenRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -299,11 +297,11 @@ func NewUserTokenRemovedEvent( } } -func UserTokenRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserTokenRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { tokenRemoved := &UserTokenRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, tokenRemoved) + err := event.Unmarshal(tokenRemoved) if err != nil { return nil, errors.ThrowInternal(err, "USER-7M9sd", "unable to unmarshal token added") } @@ -320,12 +318,12 @@ type DomainClaimedEvent struct { userLoginMustBeDomain bool } -func (e *DomainClaimedEvent) Data() interface{} { +func (e *DomainClaimedEvent) Payload() interface{} { return e } -func (e *DomainClaimedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ +func (e *DomainClaimedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ NewRemoveUsernameUniqueConstraint(e.oldUserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain), NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain), } @@ -355,11 +353,11 @@ func NewDomainClaimedEvent( } } -func DomainClaimedEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainClaimedEventMapper(event eventstore.Event) (eventstore.Event, error) { domainClaimed := &DomainClaimedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, domainClaimed) + err := event.Unmarshal(domainClaimed) if err != nil { return nil, errors.ThrowInternal(err, "USER-aR8jc", "unable to unmarshal domain claimed") } @@ -371,11 +369,11 @@ type DomainClaimedSentEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *DomainClaimedSentEvent) Data() interface{} { +func (e *DomainClaimedSentEvent) Payload() interface{} { return nil } -func (e *DomainClaimedSentEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DomainClaimedSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -392,7 +390,7 @@ func NewDomainClaimedSentEvent( } } -func DomainClaimedSentEventMapper(event *repository.Event) (eventstore.Event, error) { +func DomainClaimedSentEventMapper(event eventstore.Event) (eventstore.Event, error) { return &DomainClaimedSentEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -407,12 +405,12 @@ type UsernameChangedEvent struct { oldUserLoginMustBeDomain bool } -func (e *UsernameChangedEvent) Data() interface{} { +func (e *UsernameChangedEvent) Payload() interface{} { return e } -func (e *UsernameChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{ +func (e *UsernameChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{ NewRemoveUsernameUniqueConstraint(e.oldUserName, e.Aggregate().ResourceOwner, e.oldUserLoginMustBeDomain), NewAddUsernameUniqueConstraint(e.UserName, e.Aggregate().ResourceOwner, e.userLoginMustBeDomain), } @@ -453,11 +451,11 @@ func UsernameChangedEventWithPolicyChange() UsernameChangedEventOption { } } -func UsernameChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UsernameChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { domainClaimed := &UsernameChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, domainClaimed) + err := event.Unmarshal(domainClaimed) if err != nil { return nil, errors.ThrowInternal(err, "USER-4Bm9s", "unable to unmarshal username changed") } diff --git a/internal/repository/usergrant/user_grant.go b/internal/repository/usergrant/user_grant.go index fc887d2a3..10d6cfcf1 100644 --- a/internal/repository/usergrant/user_grant.go +++ b/internal/repository/usergrant/user_grant.go @@ -2,13 +2,11 @@ package usergrant import ( "context" - "encoding/json" "fmt" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( @@ -23,15 +21,15 @@ const ( UserGrantReactivatedType = userGrantEventTypePrefix + "reactivated" ) -func NewAddUserGrantUniqueConstraint(resourceOwner, userID, projectID, projectGrantID string) *eventstore.EventUniqueConstraint { +func NewAddUserGrantUniqueConstraint(resourceOwner, userID, projectID, projectGrantID string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueUserGrant, fmt.Sprintf("%s:%s:%s:%v", resourceOwner, userID, projectID, projectGrantID), "Errors.UserGrant.AlreadyExists") } -func NewRemoveUserGrantUniqueConstraint(resourceOwner, userID, projectID, projectGrantID string) *eventstore.EventUniqueConstraint { - return eventstore.NewRemoveEventUniqueConstraint( +func NewRemoveUserGrantUniqueConstraint(resourceOwner, userID, projectID, projectGrantID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( UniqueUserGrant, fmt.Sprintf("%s:%s:%s:%s", resourceOwner, userID, projectID, projectGrantID)) } @@ -45,12 +43,12 @@ type UserGrantAddedEvent struct { RoleKeys []string `json:"roleKeys,omitempty"` } -func (e *UserGrantAddedEvent) Data() interface{} { +func (e *UserGrantAddedEvent) Payload() interface{} { return e } -func (e *UserGrantAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.UserID, e.ProjectID, e.ProjectGrantID)} +func (e *UserGrantAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewAddUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.UserID, e.ProjectID, e.ProjectGrantID)} } func NewUserGrantAddedEvent( @@ -73,12 +71,12 @@ func NewUserGrantAddedEvent( } } -func UserGrantAddedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserGrantAddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "UGRANT-0p9ol", "unable to unmarshal user grant") } @@ -91,11 +89,11 @@ type UserGrantChangedEvent struct { RoleKeys []string `json:"roleKeys"` } -func (e *UserGrantChangedEvent) Data() interface{} { +func (e *UserGrantChangedEvent) Payload() interface{} { return e } -func (e *UserGrantChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserGrantChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -113,12 +111,12 @@ func NewUserGrantChangedEvent( } } -func UserGrantChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserGrantChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "UGRANT-4M0sd", "unable to unmarshal user grant") } @@ -131,11 +129,11 @@ type UserGrantCascadeChangedEvent struct { RoleKeys []string `json:"roleKeys,omitempty"` } -func (e *UserGrantCascadeChangedEvent) Data() interface{} { +func (e *UserGrantCascadeChangedEvent) Payload() interface{} { return e } -func (e *UserGrantCascadeChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserGrantCascadeChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -153,12 +151,12 @@ func NewUserGrantCascadeChangedEvent( } } -func UserGrantCascadeChangedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantCascadeChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { e := &UserGrantChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } - err := json.Unmarshal(event.Data, e) + err := event.Unmarshal(e) if err != nil { return nil, errors.ThrowInternal(err, "UGRANT-Gs9df", "unable to unmarshal user grant") } @@ -173,12 +171,12 @@ type UserGrantRemovedEvent struct { projectGrantID string `json:"-"` } -func (e *UserGrantRemovedEvent) Data() interface{} { +func (e *UserGrantRemovedEvent) Payload() interface{} { return nil } -func (e *UserGrantRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.userID, e.projectID, e.projectGrantID)} +func (e *UserGrantRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.userID, e.projectID, e.projectGrantID)} } func NewUserGrantRemovedEvent( @@ -200,7 +198,7 @@ func NewUserGrantRemovedEvent( } } -func UserGrantRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserGrantRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -213,12 +211,12 @@ type UserGrantCascadeRemovedEvent struct { projectGrantID string `json:"-"` } -func (e *UserGrantCascadeRemovedEvent) Data() interface{} { +func (e *UserGrantCascadeRemovedEvent) Payload() interface{} { return nil } -func (e *UserGrantCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewRemoveUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.userID, e.projectID, e.projectGrantID)} +func (e *UserGrantCascadeRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { + return []*eventstore.UniqueConstraint{NewRemoveUserGrantUniqueConstraint(e.Aggregate().ResourceOwner, e.userID, e.projectID, e.projectGrantID)} } func NewUserGrantCascadeRemovedEvent( @@ -240,7 +238,7 @@ func NewUserGrantCascadeRemovedEvent( } } -func UserGrantCascadeRemovedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantCascadeRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserGrantCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -250,11 +248,11 @@ type UserGrantDeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserGrantDeactivatedEvent) Data() interface{} { +func (e *UserGrantDeactivatedEvent) Payload() interface{} { return nil } -func (e *UserGrantDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserGrantDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -268,7 +266,7 @@ func NewUserGrantDeactivatedEvent(ctx context.Context, aggregate *eventstore.Agg } } -func UserGrantDeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserGrantDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil @@ -278,11 +276,11 @@ type UserGrantReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } -func (e *UserGrantReactivatedEvent) Data() interface{} { +func (e *UserGrantReactivatedEvent) Payload() interface{} { return nil } -func (e *UserGrantReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *UserGrantReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } @@ -296,7 +294,7 @@ func NewUserGrantReactivatedEvent(ctx context.Context, aggregate *eventstore.Agg } } -func UserGrantReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { +func UserGrantReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) { return &UserGrantReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil diff --git a/internal/user/repository/eventsourcing/model/auth_request.go b/internal/user/repository/eventsourcing/model/auth_request.go index f41aaf843..62b5fe48c 100644 --- a/internal/user/repository/eventsourcing/model/auth_request.go +++ b/internal/user/repository/eventsourcing/model/auth_request.go @@ -1,13 +1,12 @@ package model import ( - "encoding/json" "net" "github.com/zitadel/logging" caos_errs "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" ) type AuthRequest struct { @@ -23,8 +22,8 @@ type BrowserInfo struct { RemoteIP net.IP `json:"remoteIP,omitempty"` } -func (a *AuthRequest) SetData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, a); err != nil { +func (a *AuthRequest) SetData(event eventstore.Event) error { + if err := event.Unmarshal(a); err != nil { logging.Log("EVEN-T5df6").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-yGmhh", "could not unmarshal event") } diff --git a/internal/user/repository/eventsourcing/model/otp.go b/internal/user/repository/eventsourcing/model/otp.go index 5057ffaaa..f5bf36592 100644 --- a/internal/user/repository/eventsourcing/model/otp.go +++ b/internal/user/repository/eventsourcing/model/otp.go @@ -1,12 +1,11 @@ package model import ( - "encoding/json" - "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/user/model" ) @@ -22,7 +21,7 @@ type OTPVerified struct { UserAgentID string `json:"userAgentID,omitempty"` } -func (u *Human) appendOTPAddedEvent(event *es_models.Event) error { +func (u *Human) appendOTPAddedEvent(event eventstore.Event) error { u.OTP = &OTP{ State: int32(model.MFAStateNotReady), } @@ -37,17 +36,17 @@ func (u *Human) appendOTPRemovedEvent() { u.OTP = nil } -func (o *OTP) setData(event *es_models.Event) error { +func (o *OTP) setData(event eventstore.Event) error { o.ObjectRoot.AppendEvent(event) - if err := json.Unmarshal(event.Data, o); err != nil { + if err := event.Unmarshal(o); err != nil { logging.Log("EVEN-d9soe").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event") } return nil } -func (o *OTPVerified) SetData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, o); err != nil { +func (o *OTPVerified) SetData(event eventstore.Event) error { + if err := event.Unmarshal(o); err != nil { logging.Log("EVEN-BF421").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-GB6hj", "could not unmarshal event") } diff --git a/internal/user/repository/eventsourcing/model/password.go b/internal/user/repository/eventsourcing/model/password.go index 6c7414111..e9d3e58f3 100644 --- a/internal/user/repository/eventsourcing/model/password.go +++ b/internal/user/repository/eventsourcing/model/password.go @@ -1,13 +1,13 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" ) @@ -32,42 +32,42 @@ type PasswordChange struct { UserAgentID string `json:"userAgentID,omitempty"` } -func (u *Human) appendUserPasswordChangedEvent(event *es_models.Event) error { +func (u *Human) appendUserPasswordChangedEvent(event eventstore.Event) error { u.Password = new(Password) err := u.Password.setData(event) if err != nil { return err } - u.Password.ObjectRoot.CreationDate = event.CreationDate + u.Password.ObjectRoot.CreationDate = event.CreatedAt() return nil } -func (u *Human) appendPasswordSetRequestedEvent(event *es_models.Event) error { +func (u *Human) appendPasswordSetRequestedEvent(event eventstore.Event) error { u.PasswordCode = new(PasswordCode) return u.PasswordCode.SetData(event) } -func (pw *Password) setData(event *es_models.Event) error { +func (pw *Password) setData(event eventstore.Event) error { pw.ObjectRoot.AppendEvent(event) - if err := json.Unmarshal(event.Data, pw); err != nil { + if err := event.Unmarshal(pw); err != nil { logging.Log("EVEN-dks93").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-sl9xlo2rsw", "could not unmarshal event") } return nil } -func (c *PasswordCode) SetData(event *es_models.Event) error { +func (c *PasswordCode) SetData(event eventstore.Event) error { c.ObjectRoot.AppendEvent(event) - c.CreationDate = event.CreationDate - if err := json.Unmarshal(event.Data, c); err != nil { + c.CreationDate = event.CreatedAt() + if err := event.Unmarshal(c); err != nil { logging.Log("EVEN-lo0y2").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-q21dr", "could not unmarshal event") } return nil } -func (pw *PasswordChange) SetData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, pw); err != nil { +func (pw *PasswordChange) SetData(event eventstore.Event) error { + if err := event.Unmarshal(pw); err != nil { logging.Log("EVEN-ADs31").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-BDd32", "could not unmarshal event") } diff --git a/internal/user/repository/eventsourcing/model/token.go b/internal/user/repository/eventsourcing/model/token.go index cf3b46da8..57718c838 100644 --- a/internal/user/repository/eventsourcing/model/token.go +++ b/internal/user/repository/eventsourcing/model/token.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" user_repo "github.com/zitadel/zitadel/internal/repository/user" ) @@ -16,13 +15,13 @@ import ( type Token struct { es_models.ObjectRoot - TokenID string `json:"tokenId" gorm:"column:token_id"` - ApplicationID string `json:"applicationId" gorm:"column:application_id"` - UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` - Audience database.StringArray `json:"audience" gorm:"column:audience"` - Scopes database.StringArray `json:"scopes" gorm:"column:scopes"` - Expiration time.Time `json:"expiration" gorm:"column:expiration"` - PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` + TokenID string `json:"tokenId" gorm:"column:token_id"` + ApplicationID string `json:"applicationId" gorm:"column:application_id"` + UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` + Audience database.TextArray[string] `json:"audience" gorm:"column:audience"` + Scopes database.TextArray[string] `json:"scopes" gorm:"column:scopes"` + Expiration time.Time `json:"expiration" gorm:"column:expiration"` + PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` } func (t *Token) AppendEvents(events ...*es_models.Event) error { @@ -36,8 +35,7 @@ func (t *Token) AppendEvents(events ...*es_models.Event) error { } func (t *Token) AppendEvent(event *es_models.Event) error { - switch eventstore.EventType(event.Type) { - case user_repo.UserTokenAddedType: + if event.Typ == user_repo.UserTokenAddedType { err := t.setData(event) if err != nil { return err diff --git a/internal/user/repository/eventsourcing/model/user.go b/internal/user/repository/eventsourcing/model/user.go index ec5a33e2a..36cba95ea 100644 --- a/internal/user/repository/eventsourcing/model/user.go +++ b/internal/user/repository/eventsourcing/model/user.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/errors" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/user/model" @@ -40,7 +39,7 @@ func (u *User) AppendEvents(events ...*es_models.Event) error { func (u *User) AppendEvent(event *es_models.Event) error { u.ObjectRoot.AppendEvent(event) - switch eventstore.EventType(event.Type) { + switch event.Type() { case user.UserV1AddedType, user.HumanAddedType, user.MachineAddedEventType, @@ -72,11 +71,11 @@ func (u *User) AppendEvent(event *es_models.Event) error { u.Machine.user = u return u.Machine.AppendEvent(event) } - if strings.HasPrefix(string(event.Type), "user.human") || event.AggregateVersion == "v1" { + if strings.HasPrefix(string(event.Typ), "user.human") || event.AggregateVersion == "v1" { u.Human = &Human{user: u} return u.Human.AppendEvent(event) } - if strings.HasPrefix(string(event.Type), "user.machine") { + if strings.HasPrefix(string(event.Typ), "user.machine") { u.Machine = &Machine{user: u} return u.Machine.AppendEvent(event) } diff --git a/internal/user/repository/eventsourcing/model/user_human.go b/internal/user/repository/eventsourcing/model/user_human.go index 068e1af97..2df0d5bb7 100644 --- a/internal/user/repository/eventsourcing/model/user_human.go +++ b/internal/user/repository/eventsourcing/model/user_human.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/user/model" @@ -50,7 +49,7 @@ func (p *Human) AppendEvents(events ...*es_models.Event) error { } func (h *Human) AppendEvent(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { + switch event.Type() { case user.UserV1AddedType, user.UserV1RegisteredType, user.UserV1ProfileChangedType, diff --git a/internal/user/repository/eventsourcing/model/user_machine.go b/internal/user/repository/eventsourcing/model/user_machine.go index 01decb939..45d60b484 100644 --- a/internal/user/repository/eventsourcing/model/user_machine.go +++ b/internal/user/repository/eventsourcing/model/user_machine.go @@ -7,7 +7,6 @@ import ( "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" user_repo "github.com/zitadel/zitadel/internal/repository/user" ) @@ -29,7 +28,7 @@ func (sa *Machine) AppendEvents(events ...*es_models.Event) error { } func (sa *Machine) AppendEvent(event *es_models.Event) (err error) { - switch eventstore.EventType(event.Type) { + switch event.Type() { case user_repo.MachineAddedEventType, user_repo.MachineChangedEventType: err = sa.setData(event) } @@ -66,7 +65,7 @@ func (key *MachineKey) AppendEvents(events ...*es_models.Event) error { func (key *MachineKey) AppendEvent(event *es_models.Event) (err error) { key.ObjectRoot.AppendEvent(event) - switch eventstore.EventType(event.Type) { + switch event.Type() { case user_repo.MachineKeyAddedEventType: err = json.Unmarshal(event.Data, key) if err != nil { diff --git a/internal/user/repository/eventsourcing/model/web_auth_n.go b/internal/user/repository/eventsourcing/model/web_auth_n.go index 3bde19030..86bf5de06 100644 --- a/internal/user/repository/eventsourcing/model/web_auth_n.go +++ b/internal/user/repository/eventsourcing/model/web_auth_n.go @@ -1,11 +1,10 @@ package model import ( - "encoding/json" - "github.com/zitadel/logging" caos_errs "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/user/model" ) @@ -62,21 +61,21 @@ func GetWebauthn(webauthnTokens []*WebAuthNToken, id string) (int, *WebAuthNToke return -1, nil } -func (w *WebAuthNVerify) SetData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, w); err != nil { +func (w *WebAuthNVerify) SetData(event eventstore.Event) error { + if err := event.Unmarshal(w); err != nil { logging.Log("EVEN-G342rf").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-B6641", "could not unmarshal event") } return nil } -func (u *Human) appendU2FAddedEvent(event *es_models.Event) error { +func (u *Human) appendU2FAddedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { return err } - webauthn.ObjectRoot.CreationDate = event.CreationDate + webauthn.ObjectRoot.CreationDate = event.CreatedAt() webauthn.State = int32(model.MFAStateNotReady) for i, token := range u.U2FTokens { if token.State == int32(model.MFAStateNotReady) { @@ -88,7 +87,7 @@ func (u *Human) appendU2FAddedEvent(event *es_models.Event) error { return nil } -func (u *Human) appendU2FVerifiedEvent(event *es_models.Event) error { +func (u *Human) appendU2FVerifiedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -105,7 +104,7 @@ func (u *Human) appendU2FVerifiedEvent(event *es_models.Event) error { return caos_errs.ThrowPreconditionFailed(nil, "MODEL-4hu9s", "Errors.Users.MFA.U2F.NotExisting") } -func (u *Human) appendU2FChangeSignCountEvent(event *es_models.Event) error { +func (u *Human) appendU2FChangeSignCountEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -118,7 +117,7 @@ func (u *Human) appendU2FChangeSignCountEvent(event *es_models.Event) error { return caos_errs.ThrowPreconditionFailed(nil, "MODEL-5Ms8h", "Errors.Users.MFA.U2F.NotExisting") } -func (u *Human) appendU2FRemovedEvent(event *es_models.Event) error { +func (u *Human) appendU2FRemovedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -135,13 +134,13 @@ func (u *Human) appendU2FRemovedEvent(event *es_models.Event) error { return nil } -func (u *Human) appendPasswordlessAddedEvent(event *es_models.Event) error { +func (u *Human) appendPasswordlessAddedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { return err } - webauthn.ObjectRoot.CreationDate = event.CreationDate + webauthn.ObjectRoot.CreationDate = event.CreatedAt() webauthn.State = int32(model.MFAStateNotReady) for i, token := range u.PasswordlessTokens { if token.State == int32(model.MFAStateNotReady) { @@ -153,7 +152,7 @@ func (u *Human) appendPasswordlessAddedEvent(event *es_models.Event) error { return nil } -func (u *Human) appendPasswordlessVerifiedEvent(event *es_models.Event) error { +func (u *Human) appendPasswordlessVerifiedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -170,7 +169,7 @@ func (u *Human) appendPasswordlessVerifiedEvent(event *es_models.Event) error { return caos_errs.ThrowPreconditionFailed(nil, "MODEL-mKns8", "Errors.Users.MFA.Passwordless.NotExisting") } -func (u *Human) appendPasswordlessChangeSignCountEvent(event *es_models.Event) error { +func (u *Human) appendPasswordlessChangeSignCountEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -186,7 +185,7 @@ func (u *Human) appendPasswordlessChangeSignCountEvent(event *es_models.Event) e return caos_errs.ThrowPreconditionFailed(nil, "MODEL-2Mv9s", "Errors.Users.MFA.Passwordless.NotExisting") } -func (u *Human) appendPasswordlessRemovedEvent(event *es_models.Event) error { +func (u *Human) appendPasswordlessRemovedEvent(event eventstore.Event) error { webauthn := new(WebAuthNToken) err := webauthn.setData(event) if err != nil { @@ -203,23 +202,23 @@ func (u *Human) appendPasswordlessRemovedEvent(event *es_models.Event) error { return nil } -func (w *WebAuthNToken) setData(event *es_models.Event) error { +func (w *WebAuthNToken) setData(event eventstore.Event) error { w.ObjectRoot.AppendEvent(event) - if err := json.Unmarshal(event.Data, w); err != nil { + if err := event.Unmarshal(w); err != nil { logging.Log("EVEN-4M9is").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event") } return nil } -func (u *Human) appendU2FLoginEvent(event *es_models.Event) error { +func (u *Human) appendU2FLoginEvent(event eventstore.Event) error { webauthn := new(WebAuthNLogin) webauthn.ObjectRoot.AppendEvent(event) err := webauthn.setData(event) if err != nil { return err } - webauthn.ObjectRoot.CreationDate = event.CreationDate + webauthn.ObjectRoot.CreationDate = event.CreatedAt() for i, token := range u.U2FLogins { if token.AuthRequest.ID == webauthn.AuthRequest.ID { u.U2FLogins[i] = webauthn @@ -230,14 +229,14 @@ func (u *Human) appendU2FLoginEvent(event *es_models.Event) error { return nil } -func (u *Human) appendPasswordlessLoginEvent(event *es_models.Event) error { +func (u *Human) appendPasswordlessLoginEvent(event eventstore.Event) error { webauthn := new(WebAuthNLogin) webauthn.ObjectRoot.AppendEvent(event) err := webauthn.setData(event) if err != nil { return err } - webauthn.ObjectRoot.CreationDate = event.CreationDate + webauthn.ObjectRoot.CreationDate = event.CreatedAt() for i, token := range u.PasswordlessLogins { if token.AuthRequest.ID == webauthn.AuthRequest.ID { u.PasswordlessLogins[i] = webauthn @@ -248,9 +247,9 @@ func (u *Human) appendPasswordlessLoginEvent(event *es_models.Event) error { return nil } -func (w *WebAuthNLogin) setData(event *es_models.Event) error { +func (w *WebAuthNLogin) setData(event eventstore.Event) error { w.ObjectRoot.AppendEvent(event) - if err := json.Unmarshal(event.Data, w); err != nil { + if err := event.Unmarshal(w); err != nil { logging.Log("EVEN-hmSlo").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-lo023", "could not unmarshal event") } diff --git a/internal/user/repository/view/model/external_idps.go b/internal/user/repository/view/model/external_idps.go index e8b810d7a..4b03c7dc4 100644 --- a/internal/user/repository/view/model/external_idps.go +++ b/internal/user/repository/view/model/external_idps.go @@ -7,7 +7,6 @@ import ( "github.com/zitadel/logging" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" user_repo "github.com/zitadel/zitadel/internal/repository/user" ) @@ -35,10 +34,9 @@ type ExternalIDPView struct { } func (i *ExternalIDPView) AppendEvent(event *models.Event) (err error) { - i.Sequence = event.Sequence + i.Sequence = event.Seq i.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { - case user_repo.UserIDPLinkAddedType: + if event.Typ == user_repo.UserIDPLinkAddedType { i.setRootData(event) i.CreationDate = event.CreationDate err = i.SetData(event) diff --git a/internal/user/repository/view/model/notify_user.go b/internal/user/repository/view/model/notify_user.go index 7390bc504..371d7a9cd 100644 --- a/internal/user/repository/view/model/notify_user.go +++ b/internal/user/repository/view/model/notify_user.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" org_model "github.com/zitadel/zitadel/internal/org/model" "github.com/zitadel/zitadel/internal/repository/user" @@ -22,27 +21,27 @@ const ( ) type NotifyUser struct { - ID string `json:"-" gorm:"column:id;primary_key"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - UserName string `json:"userName" gorm:"column:user_name"` - LoginNames database.StringArray `json:"-" gorm:"column:login_names"` - PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` - FirstName string `json:"firstName" gorm:"column:first_name"` - LastName string `json:"lastName" gorm:"column:last_name"` - NickName string `json:"nickName" gorm:"column:nick_name"` - DisplayName string `json:"displayName" gorm:"column:display_name"` - PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` - Gender int32 `json:"gender" gorm:"column:gender"` - LastEmail string `json:"email" gorm:"column:last_email"` - VerifiedEmail string `json:"-" gorm:"column:verified_email"` - LastPhone string `json:"phone" gorm:"column:last_phone"` - VerifiedPhone string `json:"-" gorm:"column:verified_phone"` - PasswordSet bool `json:"-" gorm:"column:password_set"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - State int32 `json:"-" gorm:"-"` - InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` + ID string `json:"-" gorm:"column:id;primary_key"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + UserName string `json:"userName" gorm:"column:user_name"` + LoginNames database.TextArray[string] `json:"-" gorm:"column:login_names"` + PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` + FirstName string `json:"firstName" gorm:"column:first_name"` + LastName string `json:"lastName" gorm:"column:last_name"` + NickName string `json:"nickName" gorm:"column:nick_name"` + DisplayName string `json:"displayName" gorm:"column:display_name"` + PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` + Gender int32 `json:"gender" gorm:"column:gender"` + LastEmail string `json:"email" gorm:"column:last_email"` + VerifiedEmail string `json:"-" gorm:"column:verified_email"` + LastPhone string `json:"phone" gorm:"column:last_phone"` + VerifiedPhone string `json:"-" gorm:"column:verified_phone"` + PasswordSet bool `json:"-" gorm:"column:password_set"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + State int32 `json:"-" gorm:"-"` + InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` } func (u *NotifyUser) GenerateLoginName(domain string, appendDomain bool) string { @@ -67,8 +66,8 @@ func (u *NotifyUser) SetLoginNames(userLoginMustBeDomain bool, domains []*org_mo func (u *NotifyUser) AppendEvent(event *models.Event) (err error) { u.ChangeDate = event.CreationDate - u.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { + u.Sequence = event.Seq + switch event.Type() { case user.UserV1AddedType, user.UserV1RegisteredType, user.HumanRegisteredType, diff --git a/internal/user/repository/view/model/notify_user_test.go b/internal/user/repository/view/model/notify_user_test.go index e1726c0fa..7644ba03a 100644 --- a/internal/user/repository/view/model/notify_user_test.go +++ b/internal/user/repository/view/model/notify_user_test.go @@ -21,7 +21,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append added user event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1AddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1AddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, user: &NotifyUser{}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, @@ -29,7 +29,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append added human event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanAddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanAddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, user: &NotifyUser{}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, @@ -37,7 +37,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append change user profile event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1ProfileChangedType), ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1ProfileChangedType, ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstNameChanged", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, @@ -45,7 +45,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append change user email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1EmailChangedType), ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1EmailChangedType, ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "EmailChanged", LastPhone: "Phone"}, @@ -53,7 +53,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append change user email event, existing email", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1EmailChangedType), ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1EmailChangedType, ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", VerifiedEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "EmailChanged", VerifiedEmail: "Email", LastPhone: "Phone"}, @@ -61,7 +61,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append verify user email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1EmailVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1EmailVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", VerifiedEmail: "Email", LastPhone: "Phone"}, @@ -69,7 +69,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append change user phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PhoneChangedType), ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PhoneChangedType, ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "PhoneChanged"}, @@ -77,7 +77,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append change user phone event, existing phone", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PhoneChangedType), ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PhoneChangedType, ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone", VerifiedPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "PhoneChanged", VerifiedPhone: "Phone"}, @@ -85,7 +85,7 @@ func TestNotifyUserAppendEvent(t *testing.T) { { name: "append verify user phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PhoneVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PhoneVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone"}, }, result: &NotifyUser{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", FirstName: "FirstName", LastName: "LastName", LastEmail: "Email", LastPhone: "Phone", VerifiedPhone: "Phone"}, diff --git a/internal/user/repository/view/model/refresh_token.go b/internal/user/repository/view/model/refresh_token.go index a85aea06a..d62b3e141 100644 --- a/internal/user/repository/view/model/refresh_token.go +++ b/internal/user/repository/view/model/refresh_token.go @@ -1,7 +1,6 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" @@ -9,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" user_repo "github.com/zitadel/zitadel/internal/repository/user" usr_model "github.com/zitadel/zitadel/internal/user/model" ) @@ -25,22 +23,22 @@ const ( ) type RefreshTokenView struct { - ID string `json:"tokenId" gorm:"column:id;primary_key"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - Token string `json:"-" gorm:"column:token"` - UserID string `json:"-" gorm:"column:user_id"` - ClientID string `json:"clientID" gorm:"column:client_id"` - UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` - Audience database.StringArray `json:"audience" gorm:"column:audience"` - Scopes database.StringArray `json:"scopes" gorm:"column:scopes"` - AuthMethodsReferences database.StringArray `json:"authMethodsReference" gorm:"column:amr"` - AuthTime time.Time `json:"authTime" gorm:"column:auth_time"` - IdleExpiration time.Time `json:"-" gorm:"column:idle_expiration"` - Expiration time.Time `json:"-" gorm:"column:expiration"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` + ID string `json:"tokenId" gorm:"column:id;primary_key"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + Token string `json:"-" gorm:"column:token"` + UserID string `json:"-" gorm:"column:user_id"` + ClientID string `json:"clientID" gorm:"column:client_id"` + UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` + Audience database.TextArray[string] `json:"audience" gorm:"column:audience"` + Scopes database.TextArray[string] `json:"scopes" gorm:"column:scopes"` + AuthMethodsReferences database.TextArray[string] `json:"authMethodsReference" gorm:"column:amr"` + AuthTime time.Time `json:"authTime" gorm:"column:auth_time"` + IdleExpiration time.Time `json:"-" gorm:"column:idle_expiration"` + Expiration time.Time `json:"-" gorm:"column:expiration"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` } func RefreshTokenViewsToModel(tokens []*RefreshTokenView) []*usr_model.RefreshTokenView { @@ -71,9 +69,9 @@ func RefreshTokenViewToModel(token *RefreshTokenView) *usr_model.RefreshTokenVie } } -func (t *RefreshTokenView) AppendEventIfMyRefreshToken(event *es_models.Event) (err error) { +func (t *RefreshTokenView) AppendEventIfMyRefreshToken(event eventstore.Event) (err error) { view := new(RefreshTokenView) - switch eventstore.EventType(event.Type) { + switch event.Type() { case user_repo.HumanRefreshTokenAddedType: view.setRootData(event) err = view.appendAddedEvent(event) @@ -100,10 +98,10 @@ func (t *RefreshTokenView) AppendEventIfMyRefreshToken(event *es_models.Event) ( return nil } -func (t *RefreshTokenView) AppendEvent(event *es_models.Event) error { - t.ChangeDate = event.CreationDate - t.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { +func (t *RefreshTokenView) AppendEvent(event eventstore.Event) error { + t.ChangeDate = event.CreatedAt() + t.Sequence = event.Sequence() + switch event.Type() { case user_repo.HumanRefreshTokenAddedType: t.setRootData(event) return t.appendAddedEvent(event) @@ -114,55 +112,55 @@ func (t *RefreshTokenView) AppendEvent(event *es_models.Event) error { return nil } -func (t *RefreshTokenView) setRootData(event *es_models.Event) { - t.UserID = event.AggregateID - t.ResourceOwner = event.ResourceOwner - t.InstanceID = event.InstanceID +func (t *RefreshTokenView) setRootData(event eventstore.Event) { + t.UserID = event.Aggregate().ID + t.ResourceOwner = event.Aggregate().ResourceOwner + t.InstanceID = event.Aggregate().InstanceID } -func (t *RefreshTokenView) appendAddedEvent(event *es_models.Event) error { +func (t *RefreshTokenView) appendAddedEvent(event eventstore.Event) error { e := new(user_repo.HumanRefreshTokenAddedEvent) - if err := json.Unmarshal(event.Data, e); err != nil { + if err := event.Unmarshal(e); err != nil { logging.Log("EVEN-Dbb31").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Bbr42", "could not unmarshal event") } t.ID = e.TokenID - t.CreationDate = event.CreationDate + t.CreationDate = event.CreatedAt() t.AuthMethodsReferences = e.AuthMethodsReferences t.AuthTime = e.AuthTime t.Audience = e.Audience t.ClientID = e.ClientID - t.Expiration = event.CreationDate.Add(e.Expiration) - t.IdleExpiration = event.CreationDate.Add(e.IdleExpiration) + t.Expiration = event.CreatedAt().Add(e.Expiration) + t.IdleExpiration = event.CreatedAt().Add(e.IdleExpiration) t.Scopes = e.Scopes t.Token = e.TokenID t.UserAgentID = e.UserAgentID return nil } -func (t *RefreshTokenView) appendRenewedEvent(event *es_models.Event) error { +func (t *RefreshTokenView) appendRenewedEvent(event eventstore.Event) error { e := new(user_repo.HumanRefreshTokenRenewedEvent) - if err := json.Unmarshal(event.Data, e); err != nil { + if err := event.Unmarshal(e); err != nil { logging.Log("EVEN-Vbbn2").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-Bbrn4", "could not unmarshal event") } t.ID = e.TokenID - t.IdleExpiration = event.CreationDate.Add(e.IdleExpiration) + t.IdleExpiration = event.CreatedAt().Add(e.IdleExpiration) t.Token = e.RefreshToken return nil } -func (t *RefreshTokenView) appendRemovedEvent(event *es_models.Event) { - t.Expiration = event.CreationDate +func (t *RefreshTokenView) appendRemovedEvent(event eventstore.Event) { + t.Expiration = event.CreatedAt() } -func (t *RefreshTokenView) GetRelevantEventTypes() []es_models.EventType { - return []es_models.EventType{ - es_models.EventType(user_repo.HumanRefreshTokenAddedType), - es_models.EventType(user_repo.HumanRefreshTokenRenewedType), - es_models.EventType(user_repo.HumanRefreshTokenRemovedType), - es_models.EventType(user_repo.UserRemovedType), - es_models.EventType(user_repo.UserDeactivatedType), - es_models.EventType(user_repo.UserLockedType), +func (t *RefreshTokenView) GetRelevantEventTypes() []eventstore.EventType { + return []eventstore.EventType{ + user_repo.HumanRefreshTokenAddedType, + user_repo.HumanRefreshTokenRenewedType, + user_repo.HumanRefreshTokenRemovedType, + user_repo.UserRemovedType, + user_repo.UserDeactivatedType, + user_repo.UserLockedType, } } diff --git a/internal/user/repository/view/model/token.go b/internal/user/repository/view/model/token.go index df9d12871..4e11eec94 100644 --- a/internal/user/repository/view/model/token.go +++ b/internal/user/repository/view/model/token.go @@ -1,7 +1,6 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" @@ -9,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" user_repo "github.com/zitadel/zitadel/internal/repository/user" usr_model "github.com/zitadel/zitadel/internal/user/model" ) @@ -26,22 +24,22 @@ const ( ) type TokenView struct { - ID string `json:"tokenId" gorm:"column:id;primary_key"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - UserID string `json:"-" gorm:"column:user_id"` - ApplicationID string `json:"applicationId" gorm:"column:application_id"` - UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` - Audience database.StringArray `json:"audience" gorm:"column:audience"` - Scopes database.StringArray `json:"scopes" gorm:"column:scopes"` - Expiration time.Time `json:"expiration" gorm:"column:expiration"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` - RefreshTokenID string `json:"refreshTokenID,omitempty" gorm:"refresh_token_id"` - IsPAT bool `json:"-" gorm:"is_pat"` - Deactivated bool `json:"-" gorm:"-"` - InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` + ID string `json:"tokenId" gorm:"column:id;primary_key"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + UserID string `json:"-" gorm:"column:user_id"` + ApplicationID string `json:"applicationId" gorm:"column:application_id"` + UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` + Audience database.TextArray[string] `json:"audience" gorm:"column:audience"` + Scopes database.TextArray[string] `json:"scopes" gorm:"column:scopes"` + Expiration time.Time `json:"expiration" gorm:"column:expiration"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` + RefreshTokenID string `json:"refreshTokenID,omitempty" gorm:"refresh_token_id"` + IsPAT bool `json:"-" gorm:"is_pat"` + Deactivated bool `json:"-" gorm:"-"` + InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` } func TokenViewToModel(token *TokenView) *usr_model.TokenView { @@ -63,9 +61,9 @@ func TokenViewToModel(token *TokenView) *usr_model.TokenView { } } -func (t *TokenView) AppendEventIfMyToken(event *es_models.Event) (err error) { +func (t *TokenView) AppendEventIfMyToken(event eventstore.Event) (err error) { view := new(TokenView) - switch eventstore.EventType(event.Type) { + switch event.Type() { case user_repo.UserTokenAddedType, user_repo.PersonalAccessTokenAddedType: view.setRootData(event) @@ -91,7 +89,7 @@ func (t *TokenView) AppendEventIfMyToken(event *es_models.Event) (err error) { return nil case user_repo.UserUnlockedType, user_repo.UserReactivatedType: - if t.ID != "" && event.CreationDate.Before(t.CreationDate) { + if t.ID != "" && event.CreatedAt().Before(t.CreationDate) { t.Deactivated = false } return nil @@ -109,10 +107,10 @@ func (t *TokenView) AppendEventIfMyToken(event *es_models.Event) (err error) { return nil } -func (t *TokenView) AppendEvent(event *es_models.Event) error { - t.ChangeDate = event.CreationDate - t.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { +func (t *TokenView) AppendEvent(event eventstore.Event) error { + t.ChangeDate = event.CreatedAt() + t.Sequence = event.Sequence() + switch event.Type() { case user_repo.UserTokenAddedType, user_repo.PersonalAccessTokenAddedType: t.setRootData(event) @@ -120,36 +118,36 @@ func (t *TokenView) AppendEvent(event *es_models.Event) error { if err != nil { return err } - t.CreationDate = event.CreationDate - t.IsPAT = eventstore.EventType(event.Type) == user_repo.PersonalAccessTokenAddedType + t.CreationDate = event.CreatedAt() + t.IsPAT = event.Type() == user_repo.PersonalAccessTokenAddedType } return nil } -func (t *TokenView) setRootData(event *es_models.Event) { - t.UserID = event.AggregateID - t.ResourceOwner = event.ResourceOwner - t.InstanceID = event.InstanceID +func (t *TokenView) setRootData(event eventstore.Event) { + t.UserID = event.Aggregate().ID + t.ResourceOwner = event.Aggregate().ResourceOwner + t.InstanceID = event.Aggregate().InstanceID } -func (t *TokenView) setData(event *es_models.Event) error { - if err := json.Unmarshal(event.Data, t); err != nil { +func (t *TokenView) setData(event eventstore.Event) error { + if err := event.Unmarshal(t); err != nil { logging.Log("EVEN-3Gm9s").WithError(err).Error("could not unmarshal event data") return caos_errs.ThrowInternal(err, "MODEL-5Gms9", "could not unmarshal event") } return nil } -func agentIDFromSession(event *es_models.Event) (string, error) { +func agentIDFromSession(event eventstore.Event) (string, error) { session := make(map[string]interface{}) - if err := json.Unmarshal(event.Data, &session); err != nil { + if err := event.Unmarshal(&session); err != nil { logging.Log("EVEN-Ghgt3").WithError(err).Error("could not unmarshal event data") return "", caos_errs.ThrowInternal(nil, "MODEL-GBf32", "could not unmarshal data") } return session["userAgentID"].(string), nil } -func (t *TokenView) appendTokenRemoved(event *es_models.Event) error { +func (t *TokenView) appendTokenRemoved(event eventstore.Event) error { token, err := eventToMap(event) if err != nil { return err @@ -160,7 +158,7 @@ func (t *TokenView) appendTokenRemoved(event *es_models.Event) error { return nil } -func (t *TokenView) appendRefreshTokenRemoved(event *es_models.Event) error { +func (t *TokenView) appendRefreshTokenRemoved(event eventstore.Event) error { refreshToken, err := eventToMap(event) if err != nil { return err @@ -171,7 +169,7 @@ func (t *TokenView) appendRefreshTokenRemoved(event *es_models.Event) error { return nil } -func (t *TokenView) appendPATRemoved(event *es_models.Event) error { +func (t *TokenView) appendPATRemoved(event eventstore.Event) error { pat, err := eventToMap(event) if err != nil { return err @@ -182,26 +180,26 @@ func (t *TokenView) appendPATRemoved(event *es_models.Event) error { return nil } -func (t *TokenView) GetRelevantEventTypes() []es_models.EventType { - return []es_models.EventType{ - es_models.EventType(user_repo.UserTokenAddedType), - es_models.EventType(user_repo.PersonalAccessTokenAddedType), - es_models.EventType(user_repo.UserTokenRemovedType), - es_models.EventType(user_repo.HumanRefreshTokenRemovedType), - es_models.EventType(user_repo.UserV1SignedOutType), - es_models.EventType(user_repo.HumanSignedOutType), - es_models.EventType(user_repo.UserRemovedType), - es_models.EventType(user_repo.UserDeactivatedType), - es_models.EventType(user_repo.UserLockedType), - es_models.EventType(user_repo.UserLockedType), - es_models.EventType(user_repo.UserReactivatedType), - es_models.EventType(user_repo.PersonalAccessTokenRemovedType), +func (t *TokenView) GetRelevantEventTypes() []eventstore.EventType { + return []eventstore.EventType{ + user_repo.UserTokenAddedType, + user_repo.PersonalAccessTokenAddedType, + user_repo.UserTokenRemovedType, + user_repo.HumanRefreshTokenRemovedType, + user_repo.UserV1SignedOutType, + user_repo.HumanSignedOutType, + user_repo.UserRemovedType, + user_repo.UserDeactivatedType, + user_repo.UserLockedType, + user_repo.UserLockedType, + user_repo.UserReactivatedType, + user_repo.PersonalAccessTokenRemovedType, } } -func eventToMap(event *es_models.Event) (map[string]interface{}, error) { +func eventToMap(event eventstore.Event) (map[string]interface{}, error) { m := make(map[string]interface{}) - if err := json.Unmarshal(event.Data, &m); err != nil { + if err := event.Unmarshal(&m); err != nil { logging.Log("EVEN-Dbffe").WithError(err).Error("could not unmarshal event data") return nil, caos_errs.ThrowInternal(nil, "MODEL-SDAfw", "could not unmarshal data") } diff --git a/internal/user/repository/view/model/user.go b/internal/user/repository/view/model/user.go index db9f50371..65fd32993 100644 --- a/internal/user/repository/view/model/user.go +++ b/internal/user/repository/view/model/user.go @@ -11,7 +11,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" org_model "github.com/zitadel/zitadel/internal/org/model" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/user/model" @@ -43,18 +42,18 @@ const ( ) type UserView struct { - ID string `json:"-" gorm:"column:id;primary_key"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - State int32 `json:"-" gorm:"column:user_state"` - LastLogin time.Time `json:"-" gorm:"column:last_login"` - LoginNames database.StringArray `json:"-" gorm:"column:login_names"` - PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - Type userType `json:"-" gorm:"column:user_type"` - UserName string `json:"userName" gorm:"column:user_name"` - InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` + ID string `json:"-" gorm:"column:id;primary_key"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + State int32 `json:"-" gorm:"column:user_state"` + LastLogin time.Time `json:"-" gorm:"column:last_login"` + LoginNames database.TextArray[string] `json:"-" gorm:"column:login_names"` + PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + Type userType `json:"-" gorm:"column:user_type"` + UserName string `json:"userName" gorm:"column:user_name"` + InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` *MachineView *HumanView } @@ -236,12 +235,12 @@ func (u *UserView) SetLoginNames(userLoginMustBeDomain bool, domains []*org_mode } } -func (u *UserView) AppendEvent(event *models.Event) (err error) { - u.ChangeDate = event.CreationDate - u.Sequence = event.Sequence - switch eventstore.EventType(event.Type) { +func (u *UserView) AppendEvent(event eventstore.Event) (err error) { + u.ChangeDate = event.CreatedAt() + u.Sequence = event.Sequence() + switch event.Type() { case user.MachineAddedEventType: - u.CreationDate = event.CreationDate + u.CreationDate = event.CreatedAt() u.setRootData(event) u.Type = userTypeMachine err = u.setData(event) @@ -252,7 +251,7 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) { user.UserV1RegisteredType, user.HumanRegisteredType, user.HumanAddedType: - u.CreationDate = event.CreationDate + u.CreationDate = event.CreatedAt() u.setRootData(event) u.Type = userTypeHuman err = u.setData(event) @@ -317,14 +316,14 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) { case user.UserV1MFAOTPAddedType, user.HumanMFAOTPAddedType: if u.HumanView == nil { - logging.WithFields("sequence", event.Sequence, "instance", event.InstanceID).Warn("event is ignored because human not exists") + logging.WithFields("event_sequence", event.Sequence, "aggregate_id", event.Aggregate().ID, "instance", event.Aggregate().InstanceID).Warn("event is ignored because human not exists") return errors.ThrowInvalidArgument(nil, "MODEL-p2BXx", "event ignored: human not exists") } u.OTPState = int32(model.MFAStateNotReady) case user.UserV1MFAOTPVerifiedType, user.HumanMFAOTPVerifiedType: if u.HumanView == nil { - logging.WithFields("sequence", event.Sequence, "instance", event.InstanceID).Warn("event is ignored because human not exists") + logging.WithFields("event_sequence", event.Sequence, "aggregate_id", event.Aggregate().ID, "instance", event.Aggregate().InstanceID).Warn("event is ignored because human not exists") return errors.ThrowInvalidArgument(nil, "MODEL-o6Lcq", "event ignored: human not exists") } u.OTPState = int32(model.MFAStateReady) @@ -354,7 +353,7 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) { err = u.removeU2FToken(event) case user.UserV1MFAInitSkippedType, user.HumanMFAInitSkippedType: - u.MFAInitSkipped = event.CreationDate + u.MFAInitSkipped = event.CreatedAt() case user.UserV1InitialCodeAddedType, user.HumanInitialCodeAddedType: u.InitRequired = true @@ -367,6 +366,10 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) { u.AvatarKey = "" case user.HumanPasswordlessInitCodeAddedType, user.HumanPasswordlessInitCodeRequestedType: + if u.HumanView == nil { + logging.WithFields("event_sequence", event.Sequence, "aggregate_id", event.Aggregate().ID, "instance", event.Aggregate().InstanceID).Warn("event is ignored because human not exists") + return errors.ThrowInvalidArgument(nil, "MODEL-MbyC0", "event ignored: human not exists") + } if !u.PasswordSet { u.PasswordlessInitRequired = true u.PasswordInitRequired = false @@ -376,34 +379,34 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) { return err } -func (u *UserView) setRootData(event *models.Event) { - u.ID = event.AggregateID - u.ResourceOwner = event.ResourceOwner - u.InstanceID = event.InstanceID +func (u *UserView) setRootData(event eventstore.Event) { + u.ID = event.Aggregate().ID + u.ResourceOwner = event.Aggregate().ResourceOwner + u.InstanceID = event.Aggregate().InstanceID } -func (u *UserView) setData(event *models.Event) error { - if err := json.Unmarshal(event.Data, u); err != nil { +func (u *UserView) setData(event eventstore.Event) error { + if err := event.Unmarshal(u); err != nil { logging.Log("MODEL-lso9e").WithError(err).Error("could not unmarshal event data") return errors.ThrowInternal(nil, "MODEL-8iows", "could not unmarshal data") } return nil } -func (u *UserView) setPasswordData(event *models.Event) error { +func (u *UserView) setPasswordData(event eventstore.Event) error { password := new(es_model.Password) - if err := json.Unmarshal(event.Data, password); err != nil { + if err := event.Unmarshal(password); err != nil { logging.Log("MODEL-sdw4r").WithError(err).Error("could not unmarshal event data") return errors.ThrowInternal(nil, "MODEL-6jhsw", "could not unmarshal data") } u.PasswordSet = password.Secret != nil || password.EncodedHash != "" u.PasswordInitRequired = !u.PasswordSet u.PasswordChangeRequired = password.ChangeRequired - u.PasswordChanged = event.CreationDate + u.PasswordChanged = event.CreatedAt() return nil } -func (u *UserView) addPasswordlessToken(event *models.Event) error { +func (u *UserView) addPasswordlessToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -419,7 +422,7 @@ func (u *UserView) addPasswordlessToken(event *models.Event) error { return nil } -func (u *UserView) updatePasswordlessToken(event *models.Event) error { +func (u *UserView) updatePasswordlessToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -434,7 +437,7 @@ func (u *UserView) updatePasswordlessToken(event *models.Event) error { return nil } -func (u *UserView) removePasswordlessToken(event *models.Event) error { +func (u *UserView) removePasswordlessToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -450,7 +453,7 @@ func (u *UserView) removePasswordlessToken(event *models.Event) error { return nil } -func (u *UserView) addU2FToken(event *models.Event) error { +func (u *UserView) addU2FToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -466,7 +469,7 @@ func (u *UserView) addU2FToken(event *models.Event) error { return nil } -func (u *UserView) updateU2FToken(event *models.Event) error { +func (u *UserView) updateU2FToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -481,7 +484,7 @@ func (u *UserView) updateU2FToken(event *models.Event) error { return nil } -func (u *UserView) removeU2FToken(event *models.Event) error { +func (u *UserView) removeU2FToken(event eventstore.Event) error { token, err := webAuthNViewFromEvent(event) if err != nil { return err @@ -496,9 +499,9 @@ func (u *UserView) removeU2FToken(event *models.Event) error { return nil } -func webAuthNViewFromEvent(event *models.Event) (*WebAuthNView, error) { +func webAuthNViewFromEvent(event eventstore.Event) (*WebAuthNView, error) { token := new(WebAuthNView) - err := json.Unmarshal(event.Data, token) + err := event.Unmarshal(token) if err != nil { return nil, errors.ThrowInternal(err, "MODEL-FSaq1", "could not unmarshal data") } @@ -552,62 +555,62 @@ func (u *UserView) SetEmptyUserType() { } } -func (u *UserView) EventTypes() []models.EventType { - return []models.EventType{ - models.EventType(user.MachineAddedEventType), - models.EventType(user.UserV1AddedType), - models.EventType(user.UserV1RegisteredType), - models.EventType(user.HumanRegisteredType), - models.EventType(user.HumanAddedType), - models.EventType(user.UserRemovedType), - models.EventType(user.UserV1PasswordChangedType), - models.EventType(user.HumanPasswordChangedType), - models.EventType(user.HumanPasswordlessTokenAddedType), - models.EventType(user.HumanPasswordlessTokenVerifiedType), - models.EventType(user.HumanPasswordlessTokenRemovedType), - models.EventType(user.UserV1ProfileChangedType), - models.EventType(user.HumanProfileChangedType), - models.EventType(user.UserV1AddressChangedType), - models.EventType(user.HumanAddressChangedType), - models.EventType(user.MachineChangedEventType), - models.EventType(user.UserDomainClaimedType), - models.EventType(user.UserUserNameChangedType), - models.EventType(user.UserV1EmailChangedType), - models.EventType(user.HumanEmailChangedType), - models.EventType(user.UserV1EmailVerifiedType), - models.EventType(user.HumanEmailVerifiedType), - models.EventType(user.UserV1PhoneChangedType), - models.EventType(user.HumanPhoneChangedType), - models.EventType(user.UserV1PhoneVerifiedType), - models.EventType(user.HumanPhoneVerifiedType), - models.EventType(user.UserV1PhoneRemovedType), - models.EventType(user.HumanPhoneRemovedType), - models.EventType(user.UserDeactivatedType), - models.EventType(user.UserReactivatedType), - models.EventType(user.UserUnlockedType), - models.EventType(user.UserLockedType), - models.EventType(user.UserV1MFAOTPAddedType), - models.EventType(user.HumanMFAOTPAddedType), - models.EventType(user.UserV1MFAOTPVerifiedType), - models.EventType(user.HumanMFAOTPVerifiedType), - models.EventType(user.UserV1MFAOTPRemovedType), - models.EventType(user.HumanMFAOTPRemovedType), - models.EventType(user.HumanOTPSMSAddedType), - models.EventType(user.HumanOTPSMSRemovedType), - models.EventType(user.HumanOTPEmailAddedType), - models.EventType(user.HumanOTPEmailRemovedType), - models.EventType(user.HumanU2FTokenAddedType), - models.EventType(user.HumanU2FTokenVerifiedType), - models.EventType(user.HumanU2FTokenRemovedType), - models.EventType(user.UserV1MFAInitSkippedType), - models.EventType(user.HumanMFAInitSkippedType), - models.EventType(user.UserV1InitialCodeAddedType), - models.EventType(user.HumanInitialCodeAddedType), - models.EventType(user.UserV1InitializedCheckSucceededType), - models.EventType(user.HumanInitializedCheckSucceededType), - models.EventType(user.HumanAvatarAddedType), - models.EventType(user.HumanAvatarRemovedType), - models.EventType(user.HumanPasswordlessInitCodeAddedType), - models.EventType(user.HumanPasswordlessInitCodeRequestedType), +func (u *UserView) EventTypes() []eventstore.EventType { + return []eventstore.EventType{ + user.MachineAddedEventType, + user.UserV1AddedType, + user.UserV1RegisteredType, + user.HumanRegisteredType, + user.HumanAddedType, + user.UserRemovedType, + user.UserV1PasswordChangedType, + user.HumanPasswordChangedType, + user.HumanPasswordlessTokenAddedType, + user.HumanPasswordlessTokenVerifiedType, + user.HumanPasswordlessTokenRemovedType, + user.UserV1ProfileChangedType, + user.HumanProfileChangedType, + user.UserV1AddressChangedType, + user.HumanAddressChangedType, + user.MachineChangedEventType, + user.UserDomainClaimedType, + user.UserUserNameChangedType, + user.UserV1EmailChangedType, + user.HumanEmailChangedType, + user.UserV1EmailVerifiedType, + user.HumanEmailVerifiedType, + user.UserV1PhoneChangedType, + user.HumanPhoneChangedType, + user.UserV1PhoneVerifiedType, + user.HumanPhoneVerifiedType, + user.UserV1PhoneRemovedType, + user.HumanPhoneRemovedType, + user.UserDeactivatedType, + user.UserReactivatedType, + user.UserUnlockedType, + user.UserLockedType, + user.UserV1MFAOTPAddedType, + user.HumanMFAOTPAddedType, + user.UserV1MFAOTPVerifiedType, + user.HumanMFAOTPVerifiedType, + user.UserV1MFAOTPRemovedType, + user.HumanMFAOTPRemovedType, + user.HumanOTPSMSAddedType, + user.HumanOTPSMSRemovedType, + user.HumanOTPEmailAddedType, + user.HumanOTPEmailRemovedType, + user.HumanU2FTokenAddedType, + user.HumanU2FTokenVerifiedType, + user.HumanU2FTokenRemovedType, + user.UserV1MFAInitSkippedType, + user.HumanMFAInitSkippedType, + user.UserV1InitialCodeAddedType, + user.HumanInitialCodeAddedType, + user.UserV1InitializedCheckSucceededType, + user.HumanInitializedCheckSucceededType, + user.HumanAvatarAddedType, + user.HumanAvatarRemovedType, + user.HumanPasswordlessInitCodeAddedType, + user.HumanPasswordlessInitCodeRequestedType, } } diff --git a/internal/user/repository/view/model/user_membership.go b/internal/user/repository/view/model/user_membership.go index c8872d683..7562c1e84 100644 --- a/internal/user/repository/view/model/user_membership.go +++ b/internal/user/repository/view/model/user_membership.go @@ -8,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/database" caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/v1/models" iam_es_model "github.com/zitadel/zitadel/internal/iam/repository/eventsourcing/model" org_es_model "github.com/zitadel/zitadel/internal/org/repository/eventsourcing/model" @@ -34,21 +33,21 @@ type UserMembershipView struct { AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"` ObjectID string `json:"-" gorm:"column:object_id;primary_key"` - Roles database.StringArray `json:"-" gorm:"column:roles"` - DisplayName string `json:"-" gorm:"column:display_name"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` + Roles database.TextArray[string] `json:"-" gorm:"column:roles"` + DisplayName string `json:"-" gorm:"column:display_name"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` } func (u *UserMembershipView) AppendEvent(event *models.Event) (err error) { u.ChangeDate = event.CreationDate - u.Sequence = event.Sequence + u.Sequence = event.Seq - switch eventstore.EventType(event.Type) { + switch event.Type() { case instance.MemberAddedEventType: u.setRootData(event, model.MemberTypeIam) err = u.setIamMemberData(event) diff --git a/internal/user/repository/view/model/user_session.go b/internal/user/repository/view/model/user_session.go index 63eb5e0a7..d660b38a2 100644 --- a/internal/user/repository/view/model/user_session.go +++ b/internal/user/repository/view/model/user_session.go @@ -1,7 +1,6 @@ package model import ( - "encoding/json" "time" "github.com/zitadel/logging" @@ -9,7 +8,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/user/model" es_model "github.com/zitadel/zitadel/internal/user/repository/eventsourcing/model" @@ -47,9 +45,9 @@ type UserSessionView struct { InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"` } -func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) { +func UserSessionFromEvent(event eventstore.Event) (*UserSessionView, error) { v := new(UserSessionView) - if err := json.Unmarshal(event.Data, v); err != nil { + if err := event.Unmarshal(v); err != nil { logging.Log("EVEN-lso9e").WithError(err).Error("could not unmarshal event data") return nil, caos_errs.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data") } @@ -88,13 +86,13 @@ func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionVi return result } -func (v *UserSessionView) AppendEvent(event *models.Event) error { - v.Sequence = event.Sequence - v.ChangeDate = event.CreationDate - switch eventstore.EventType(event.Type) { +func (v *UserSessionView) AppendEvent(event eventstore.Event) error { + v.Sequence = event.Sequence() + v.ChangeDate = event.CreatedAt() + switch event.Type() { case user.UserV1PasswordCheckSucceededType, user.HumanPasswordCheckSucceededType: - v.PasswordVerification = event.CreationDate + v.PasswordVerification = event.CreatedAt() v.State = int32(domain.UserSessionStateActive) case user.UserIDPLoginCheckSucceededType: data := new(es_model.AuthRequest) @@ -102,12 +100,12 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { if err != nil { return err } - v.ExternalLoginVerification = event.CreationDate + v.ExternalLoginVerification = event.CreatedAt() v.SelectedIDPConfigID = data.SelectedIDPConfigID v.State = int32(domain.UserSessionStateActive) case user.HumanPasswordlessTokenCheckSucceededType: - v.PasswordlessVerification = event.CreationDate - v.MultiFactorVerification = event.CreationDate + v.PasswordlessVerification = event.CreatedAt() + v.MultiFactorVerification = event.CreatedAt() v.MultiFactorVerificationType = int32(domain.MFATypeU2FUserVerification) v.State = int32(domain.UserSessionStateActive) case user.HumanPasswordlessTokenCheckFailedType, @@ -134,11 +132,11 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { return err } if v.UserAgentID == data.UserAgentID { - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeTOTP) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeTOTP) } case user.UserV1MFAOTPCheckSucceededType, user.HumanMFAOTPCheckSucceededType: - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeTOTP) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeTOTP) case user.HumanOTPSMSCheckSucceededType: data := new(es_model.OTPVerified) err := data.SetData(event) @@ -146,7 +144,7 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { return err } if v.UserAgentID == data.UserAgentID { - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeOTPSMS) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeOTPSMS) } case user.HumanOTPEmailCheckSucceededType: data := new(es_model.OTPVerified) @@ -155,7 +153,7 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { return err } if v.UserAgentID == data.UserAgentID { - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeOTPEmail) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeOTPEmail) } case user.UserV1MFAOTPCheckFailedType, user.UserV1MFAOTPRemovedType, @@ -173,10 +171,10 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { return err } if v.UserAgentID == data.UserAgentID { - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeU2F) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeU2F) } case user.HumanU2FTokenCheckSucceededType: - v.setSecondFactorVerification(event.CreationDate, domain.MFATypeU2F) + v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeU2F) case user.UserV1SignedOutType, user.HumanSignedOutType, user.UserLockedType, @@ -210,49 +208,49 @@ func (v *UserSessionView) setSecondFactorVerification(verificationTime time.Time v.State = int32(domain.UserSessionStateActive) } -func avatarKeyFromEvent(event *models.Event) (string, error) { +func avatarKeyFromEvent(event eventstore.Event) (string, error) { data := make(map[string]string) - if err := json.Unmarshal(event.Data, &data); err != nil { + if err := event.Unmarshal(&data); err != nil { logging.Log("EVEN-Sfew2").WithError(err).Error("could not unmarshal event data") return "", caos_errs.ThrowInternal(err, "MODEL-SFw2q", "could not unmarshal event") } return data["storeKey"], nil } -func (v *UserSessionView) EventTypes() []models.EventType { - return []models.EventType{ - models.EventType(user.UserV1PasswordCheckSucceededType), - models.EventType(user.HumanPasswordCheckSucceededType), - models.EventType(user.UserIDPLoginCheckSucceededType), - models.EventType(user.HumanPasswordlessTokenCheckSucceededType), - models.EventType(user.HumanPasswordlessTokenCheckFailedType), - models.EventType(user.HumanPasswordlessTokenRemovedType), - models.EventType(user.UserV1PasswordCheckFailedType), - models.EventType(user.HumanPasswordCheckFailedType), - models.EventType(user.UserV1PasswordChangedType), - models.EventType(user.HumanPasswordChangedType), - models.EventType(user.HumanMFAOTPVerifiedType), - models.EventType(user.UserV1MFAOTPCheckSucceededType), - models.EventType(user.HumanMFAOTPCheckSucceededType), - models.EventType(user.UserV1MFAOTPCheckFailedType), - models.EventType(user.UserV1MFAOTPRemovedType), - models.EventType(user.HumanMFAOTPCheckFailedType), - models.EventType(user.HumanMFAOTPRemovedType), - models.EventType(user.HumanOTPSMSCheckSucceededType), - models.EventType(user.HumanOTPSMSCheckFailedType), - models.EventType(user.HumanOTPEmailCheckSucceededType), - models.EventType(user.HumanOTPEmailCheckFailedType), - models.EventType(user.HumanU2FTokenCheckFailedType), - models.EventType(user.HumanU2FTokenRemovedType), - models.EventType(user.HumanU2FTokenVerifiedType), - models.EventType(user.HumanU2FTokenCheckSucceededType), - models.EventType(user.UserV1SignedOutType), - models.EventType(user.HumanSignedOutType), - models.EventType(user.UserLockedType), - models.EventType(user.UserDeactivatedType), - models.EventType(user.UserIDPLinkRemovedType), - models.EventType(user.UserIDPLinkCascadeRemovedType), - models.EventType(user.HumanAvatarAddedType), - models.EventType(user.HumanAvatarRemovedType), +func (v *UserSessionView) EventTypes() []eventstore.EventType { + return []eventstore.EventType{ + user.UserV1PasswordCheckSucceededType, + user.HumanPasswordCheckSucceededType, + user.UserIDPLoginCheckSucceededType, + user.HumanPasswordlessTokenCheckSucceededType, + user.HumanPasswordlessTokenCheckFailedType, + user.HumanPasswordlessTokenRemovedType, + user.UserV1PasswordCheckFailedType, + user.HumanPasswordCheckFailedType, + user.UserV1PasswordChangedType, + user.HumanPasswordChangedType, + user.HumanMFAOTPVerifiedType, + user.UserV1MFAOTPCheckSucceededType, + user.HumanMFAOTPCheckSucceededType, + user.UserV1MFAOTPCheckFailedType, + user.UserV1MFAOTPRemovedType, + user.HumanMFAOTPCheckFailedType, + user.HumanMFAOTPRemovedType, + user.HumanOTPSMSCheckSucceededType, + user.HumanOTPSMSCheckFailedType, + user.HumanOTPEmailCheckSucceededType, + user.HumanOTPEmailCheckFailedType, + user.HumanU2FTokenCheckFailedType, + user.HumanU2FTokenRemovedType, + user.HumanU2FTokenVerifiedType, + user.HumanU2FTokenCheckSucceededType, + user.UserV1SignedOutType, + user.HumanSignedOutType, + user.UserLockedType, + user.UserDeactivatedType, + user.UserIDPLinkRemovedType, + user.UserIDPLinkCascadeRemovedType, + user.HumanAvatarAddedType, + user.HumanAvatarRemovedType, } } diff --git a/internal/user/repository/view/model/user_session_test.go b/internal/user/repository/view/model/user_session_test.go index 3ba7ab5e6..8f3157f98 100644 --- a/internal/user/repository/view/model/user_session_test.go +++ b/internal/user/repository/view/model/user_session_test.go @@ -30,7 +30,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user password check succeeded event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1PasswordCheckSucceededType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1PasswordCheckSucceededType}, userView: &UserSessionView{}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: now()}, @@ -38,7 +38,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human password check succeeded event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanPasswordCheckSucceededType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanPasswordCheckSucceededType}, userView: &UserSessionView{}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: now()}, @@ -46,7 +46,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user password check failed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1PasswordCheckFailedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1PasswordCheckFailedType}, userView: &UserSessionView{PasswordVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}}, @@ -54,7 +54,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human password check failed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanPasswordCheckFailedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanPasswordCheckFailedType}, userView: &UserSessionView{PasswordVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}}, @@ -64,7 +64,7 @@ func TestAppendEvent(t *testing.T) { args: args{ event: &es_models.Event{ CreationDate: now(), - Type: es_models.EventType(user.UserV1PasswordChangedType), + Typ: user.UserV1PasswordChangedType, Data: func() []byte { d, _ := json.Marshal(&es_model.Password{ Secret: &crypto.CryptoValue{Crypted: []byte("test")}, @@ -81,7 +81,7 @@ func TestAppendEvent(t *testing.T) { args: args{ event: &es_models.Event{ CreationDate: now(), - Type: es_models.EventType(user.HumanPasswordChangedType), + Typ: user.HumanPasswordChangedType, Data: func() []byte { d, _ := json.Marshal(&es_model.PasswordChange{ Password: es_model.Password{ @@ -100,7 +100,7 @@ func TestAppendEvent(t *testing.T) { args: args{ event: &es_models.Event{ CreationDate: now(), - Type: es_models.EventType(user.HumanPasswordChangedType), + Typ: user.HumanPasswordChangedType, Data: func() []byte { d, _ := json.Marshal(&es_model.PasswordChange{ Password: es_model.Password{ @@ -120,7 +120,7 @@ func TestAppendEvent(t *testing.T) { args: args{ event: &es_models.Event{ CreationDate: now(), - Type: es_models.EventType(user.HumanMFAOTPVerifiedType), + Typ: user.HumanMFAOTPVerifiedType, Data: nil, }, userView: &UserSessionView{UserAgentID: "id"}, @@ -132,7 +132,7 @@ func TestAppendEvent(t *testing.T) { args: args{ event: &es_models.Event{ CreationDate: now(), - Type: es_models.EventType(user.HumanMFAOTPVerifiedType), + Typ: user.HumanMFAOTPVerifiedType, Data: func() []byte { d, _ := json.Marshal(&es_model.OTPVerified{ UserAgentID: "id", @@ -147,7 +147,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user otp check succeeded event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1MFAOTPCheckSucceededType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPCheckSucceededType}, userView: &UserSessionView{}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: now()}, @@ -155,7 +155,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human otp check succeeded event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanMFAOTPCheckSucceededType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPCheckSucceededType}, userView: &UserSessionView{}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: now()}, @@ -163,7 +163,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user otp check failed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1MFAOTPCheckFailedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPCheckFailedType}, userView: &UserSessionView{SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}}, @@ -171,7 +171,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human otp check failed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanMFAOTPCheckFailedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPCheckFailedType}, userView: &UserSessionView{SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}}, @@ -179,7 +179,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user otp removed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1MFAOTPRemovedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPRemovedType}, userView: &UserSessionView{SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}}, @@ -187,7 +187,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human otp removed event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanMFAOTPRemovedType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPRemovedType}, userView: &UserSessionView{SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}}, @@ -195,7 +195,7 @@ func TestAppendEvent(t *testing.T) { { name: "append user signed out event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.UserV1SignedOutType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.UserV1SignedOutType}, userView: &UserSessionView{PasswordVerification: now(), SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}, SecondFactorVerification: time.Time{}, State: 1}, @@ -203,7 +203,7 @@ func TestAppendEvent(t *testing.T) { { name: "append human signed out event", args: args{ - event: &es_models.Event{CreationDate: now(), Type: es_models.EventType(user.HumanSignedOutType)}, + event: &es_models.Event{CreationDate: now(), Typ: user.HumanSignedOutType}, userView: &UserSessionView{PasswordVerification: now(), SecondFactorVerification: now()}, }, result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}, SecondFactorVerification: time.Time{}, State: 1}, diff --git a/internal/user/repository/view/model/user_test.go b/internal/user/repository/view/model/user_test.go index 6fa5261cc..36f19742c 100644 --- a/internal/user/repository/view/model/user_test.go +++ b/internal/user/repository/view/model/user_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/eventstore" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/user/model" @@ -76,7 +77,7 @@ func getFullMachine() *es_model.User { func TestUserAppendEvent(t *testing.T) { type args struct { - event *es_models.Event + event eventstore.Event user *UserView } tests := []struct { @@ -87,7 +88,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added user event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1AddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1AddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, @@ -95,7 +96,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added human event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanAddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanAddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(nil))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, @@ -103,7 +104,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added machine event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.MachineAddedEventType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullMachine())}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.MachineAddedEventType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullMachine())}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", MachineView: &MachineView{Description: "Description", Name: "Machine"}, State: int32(model.UserStateActive)}, @@ -111,7 +112,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added user with password event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1AddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{Secret: &crypto.CryptoValue{}}))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1AddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{Secret: &crypto.CryptoValue{}}))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", PasswordSet: true}, State: int32(model.UserStateInitial)}, @@ -119,7 +120,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added human with password event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanAddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{Secret: &crypto.CryptoValue{}}))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanAddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{Secret: &crypto.CryptoValue{}}))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", PasswordSet: true}, State: int32(model.UserStateInitial)}, @@ -127,7 +128,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added user with password but change required event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1AddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}}))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1AddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}}))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", PasswordSet: true, PasswordChangeRequired: true}, State: int32(model.UserStateInitial)}, @@ -135,7 +136,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append added human with password but change required event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanAddedType), ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}}))}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanAddedType, ResourceOwner: "GrantedOrgID", Data: mockUserData(getFullHuman(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}}))}, user: &UserView{}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", PasswordSet: true, PasswordChangeRequired: true}, State: int32(model.UserStateInitial)}, @@ -143,7 +144,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append password change event on user", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PasswordChangedType), ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{Secret: &crypto.CryptoValue{}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PasswordChangedType, ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{Secret: &crypto.CryptoValue{}})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", PasswordSet: true}, State: int32(model.UserStateActive)}, @@ -151,7 +152,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append password change event on human", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanPasswordChangedType), ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{Secret: &crypto.CryptoValue{}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanPasswordChangedType, ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{Secret: &crypto.CryptoValue{}})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", PasswordSet: true}, State: int32(model.UserStateActive)}, @@ -159,7 +160,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append password change with change required event on user", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PasswordChangedType), ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PasswordChangedType, ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", PasswordSet: true, PasswordChangeRequired: true}, State: int32(model.UserStateActive)}, @@ -167,7 +168,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append password change with change required event on human", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanPasswordChangedType), ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanPasswordChangedType, ResourceOwner: "GrantedOrgID", Data: mockPasswordData(&es_model.Password{ChangeRequired: true, Secret: &crypto.CryptoValue{}})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country", PasswordSet: true, PasswordChangeRequired: true}, State: int32(model.UserStateActive)}, @@ -175,7 +176,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change user profile event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1ProfileChangedType), ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1ProfileChangedType, ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstNameChanged", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, @@ -183,7 +184,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change human profile event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanProfileChangedType), ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanProfileChangedType, ResourceOwner: "GrantedOrgID", Data: mockProfileData(&es_model.Profile{FirstName: "FirstNameChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstNameChanged", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, @@ -191,7 +192,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change user email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1EmailChangedType), ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1EmailChangedType, ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "EmailChanged", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -199,7 +200,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change human email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanEmailChangedType), ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanEmailChangedType, ResourceOwner: "GrantedOrgID", Data: mockEmailData(&es_model.Email{EmailAddress: "EmailChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "EmailChanged", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -207,7 +208,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append verify user email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1EmailVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1EmailVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -215,7 +216,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append verify human email event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanEmailVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanEmailVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInitial)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -223,7 +224,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change user phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PhoneChangedType), ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PhoneChangedType, ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "PhoneChanged", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -231,7 +232,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change human phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanPhoneChangedType), ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanPhoneChangedType, ResourceOwner: "GrantedOrgID", Data: mockPhoneData(&es_model.Phone{PhoneNumber: "PhoneChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "PhoneChanged", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -239,7 +240,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append verify user phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1PhoneVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1PhoneVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", IsPhoneVerified: true, Country: "Country"}, State: int32(model.UserStateActive)}, @@ -247,7 +248,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append verify human phone event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanPhoneVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanPhoneVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", IsPhoneVerified: true, Country: "Country"}, State: int32(model.UserStateActive)}, @@ -255,7 +256,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change user address event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1AddressChangedType), ResourceOwner: "GrantedOrgID", Data: mockAddressData(&es_model.Address{Country: "CountryChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1AddressChangedType, ResourceOwner: "GrantedOrgID", Data: mockAddressData(&es_model.Address{Country: "CountryChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "CountryChanged"}, State: int32(model.UserStateActive)}, @@ -263,7 +264,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append change human address event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanAddressChangedType), ResourceOwner: "GrantedOrgID", Data: mockAddressData(&es_model.Address{Country: "CountryChanged"})}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanAddressChangedType, ResourceOwner: "GrantedOrgID", Data: mockAddressData(&es_model.Address{Country: "CountryChanged"})}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", IsEmailVerified: true, Phone: "Phone", Country: "CountryChanged"}, State: int32(model.UserStateActive)}, @@ -271,7 +272,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user deactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserDeactivatedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserDeactivatedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInactive)}, @@ -279,7 +280,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user reactivate event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserReactivatedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserReactivatedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateInactive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -287,7 +288,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user lock event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserLockedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserLockedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateLocked)}, @@ -295,7 +296,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user unlock event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserUnlockedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserUnlockedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateLocked)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, @@ -303,7 +304,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user add otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1MFAOTPAddedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1MFAOTPAddedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateNotReady)}, State: int32(model.UserStateActive)}, @@ -311,7 +312,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append human add otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanMFAOTPAddedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanMFAOTPAddedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateNotReady)}, State: int32(model.UserStateActive)}, @@ -319,7 +320,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user verify otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1MFAOTPVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1MFAOTPVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateNotReady)}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateReady)}, State: int32(model.UserStateActive)}, @@ -327,7 +328,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append human verify otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanMFAOTPVerifiedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanMFAOTPVerifiedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateNotReady)}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateReady)}, State: int32(model.UserStateActive)}, @@ -335,7 +336,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user remove otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.UserV1MFAOTPRemovedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.UserV1MFAOTPRemovedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateReady)}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateUnspecified)}, State: int32(model.UserStateActive)}, @@ -343,7 +344,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append human remove otp event", args: args{ - event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: es_models.EventType(user.HumanMFAOTPRemovedType), ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{AggregateID: "AggregateID", Seq: 1, Typ: user.HumanMFAOTPRemovedType, ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateReady)}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", OTPState: int32(model.MFAStateUnspecified)}, State: int32(model.UserStateActive)}, @@ -351,7 +352,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append user mfa init skipped event", args: args{ - event: &es_models.Event{Sequence: 1, CreationDate: time.Now().UTC(), Type: es_models.EventType(user.UserV1MFAInitSkippedType), AggregateID: "AggregateID", ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{Seq: 1, CreationDate: time.Now().UTC(), Typ: user.UserV1MFAInitSkippedType, AggregateID: "AggregateID", ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", MFAInitSkipped: time.Now().UTC()}, State: int32(model.UserStateActive)}, @@ -359,7 +360,7 @@ func TestUserAppendEvent(t *testing.T) { { name: "append human mfa init skipped event", args: args{ - event: &es_models.Event{Sequence: 1, CreationDate: time.Now().UTC(), Type: es_models.EventType(user.HumanMFAInitSkippedType), AggregateID: "AggregateID", ResourceOwner: "GrantedOrgID"}, + event: &es_models.Event{Seq: 1, CreationDate: time.Now().UTC(), Typ: user.HumanMFAInitSkippedType, AggregateID: "AggregateID", ResourceOwner: "GrantedOrgID"}, user: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country"}, State: int32(model.UserStateActive)}, }, result: &UserView{ID: "AggregateID", ResourceOwner: "GrantedOrgID", UserName: "UserName", HumanView: &HumanView{FirstName: "FirstName", LastName: "LastName", Email: "Email", Phone: "Phone", Country: "Country", MFAInitSkipped: time.Now().UTC()}, State: int32(model.UserStateActive)}, diff --git a/internal/user/repository/view/query.go b/internal/user/repository/view/query.go index c35188a22..eb362a378 100644 --- a/internal/user/repository/view/query.go +++ b/internal/user/repository/view/query.go @@ -2,20 +2,21 @@ package view import ( "github.com/zitadel/zitadel/internal/errors" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/user" ) -func UserByIDQuery(id, instanceID string, latestSequence uint64, eventTypes []es_models.EventType) (*es_models.SearchQuery, error) { +func UserByIDQuery(id, instanceID string, sequence uint64, eventTypes []eventstore.EventType) (*eventstore.SearchQueryBuilder, error) { if id == "" { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d8isw", "Errors.User.UserIDMissing") } - return es_models.NewSearchQuery(). + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + AwaitOpenTransactions(). + InstanceID(instanceID). + SequenceGreater(sequence). AddQuery(). - AggregateTypeFilter(user.AggregateType). - AggregateIDFilter(id). - EventTypesFilter(eventTypes...). - LatestSequenceFilter(latestSequence). - InstanceIDFilter(instanceID). - SearchQuery(), nil + AggregateTypes(user.AggregateType). + AggregateIDs(id). + EventTypes(eventTypes...). + Builder(), nil } diff --git a/internal/user/repository/view/user_view.go b/internal/user/repository/view/user_view.go index f70fcf393..c0b5d2af7 100644 --- a/internal/user/repository/view/user_view.go +++ b/internal/user/repository/view/user_view.go @@ -144,76 +144,6 @@ func UsersByOrgID(db *gorm.DB, table, orgID, instanceID string) ([]*model.UserVi return users, err } -func UserIDsByDomain(db *gorm.DB, table, orgDomain, instanceID string) ([]string, error) { - type id struct { - Id string - } - ids := make([]id, 0) - orgIDQuery := &usr_model.UserSearchQuery{ - Key: usr_model.UserSearchKeyUserName, - Method: domain.SearchMethodEndsWithIgnoreCase, - Value: "%" + orgDomain, - } - instanceIDQuery := &usr_model.UserSearchQuery{ - Key: usr_model.UserSearchKeyInstanceID, - Method: domain.SearchMethodEquals, - Value: instanceID, - } - ownerRemovedQuery := &usr_model.UserSearchQuery{ - Key: usr_model.UserSearchOwnerRemoved, - Method: domain.SearchMethodEquals, - Value: false, - } - query := repository.PrepareSearchQuery(table, model.UserSearchRequest{ - Queries: []*usr_model.UserSearchQuery{orgIDQuery, instanceIDQuery, ownerRemovedQuery}, - }) - _, err := query(db, &ids) - if err != nil { - return nil, err - } - users := make([]string, len(ids)) - for i, id := range ids { - users[i] = id.Id - } - return users, err -} - -func SearchUsers(db *gorm.DB, table string, req *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) { - users := make([]*model.UserView, 0) - query := repository.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries}) - count, err := query(db, &users) - if err != nil { - return nil, 0, err - } - return users, count, nil -} - -func GetGlobalUserByLoginName(db *gorm.DB, table, loginName, instanceID string) (*model.UserView, error) { - user := new(model.UserView) - query := repository.PrepareGetByQuery(table, - &model.UserSearchQuery{Key: usr_model.UserSearchKeyLoginNames, Value: loginName, Method: domain.SearchMethodListContains}, - &model.UserSearchQuery{Key: usr_model.UserSearchKeyInstanceID, Value: instanceID, Method: domain.SearchMethodEquals}, - &model.UserSearchQuery{Key: usr_model.UserSearchOwnerRemoved, Value: false, Method: domain.SearchMethodEquals}, - ) - err := query(db, user) - if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-8uWer", "Errors.User.NotFound") - } - user.SetEmptyUserType() - return user, err -} - -func UserMFAs(db *gorm.DB, table, userID, instanceID string) ([]*usr_model.MultiFactor, error) { - user, err := UserByID(db, table, userID, instanceID) - if err != nil { - return nil, err - } - if user.OTPState == int32(usr_model.MFAStateUnspecified) { - return []*usr_model.MultiFactor{}, nil - } - return []*usr_model.MultiFactor{{Type: usr_model.MFATypeOTP, State: usr_model.MFAState(user.OTPState)}}, nil -} - func PutUsers(db *gorm.DB, table string, users ...*model.UserView) error { save := repository.PrepareBulkSave(table) u := make([]interface{}, len(users)) diff --git a/internal/view/model/failed_event.go b/internal/view/model/failed_event.go deleted file mode 100644 index f270ae419..000000000 --- a/internal/view/model/failed_event.go +++ /dev/null @@ -1,13 +0,0 @@ -package model - -import "time" - -type FailedEvent struct { - Database string - ViewName string - FailedSequence uint64 - FailureCount uint64 - ErrMsg string - InstanceID string - LastFailed time.Time -} diff --git a/internal/view/model/general_query.go b/internal/view/model/general_query.go deleted file mode 100644 index c528f1dda..000000000 --- a/internal/view/model/general_query.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -import ( - "github.com/zitadel/zitadel/internal/domain" -) - -type GeneralSearchRequest struct { - Offset uint64 - Limit uint64 - SortingColumn GeneralSearchKey - Asc bool - Queries []*GeneralSearchQuery -} - -type GeneralSearchKey int32 - -const ( - GeneralSearchKeyUnspecified GeneralSearchKey = iota -) - -type GeneralSearchQuery struct { - Key GeneralSearchKey - Method domain.SearchMethod - Value interface{} -} diff --git a/internal/view/model/view.go b/internal/view/model/view.go deleted file mode 100644 index 6f766b912..000000000 --- a/internal/view/model/view.go +++ /dev/null @@ -1,13 +0,0 @@ -package model - -import ( - "time" -) - -type View struct { - Database string - ViewName string - CurrentSequence uint64 - EventTimestamp time.Time - LastSuccessfulSpoolerRun time.Time -} diff --git a/internal/view/repository/failed_events.go b/internal/view/repository/failed_events.go deleted file mode 100644 index 6eb75c203..000000000 --- a/internal/view/repository/failed_events.go +++ /dev/null @@ -1,180 +0,0 @@ -package repository - -import ( - "strings" - "time" - - "github.com/jinzhu/gorm" - - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/errors" - view_model "github.com/zitadel/zitadel/internal/view/model" -) - -type FailedEvent struct { - ViewName string `gorm:"column:view_name;primary_key"` - FailedSequence uint64 `gorm:"column:failed_sequence;primary_key"` - FailureCount uint64 `gorm:"column:failure_count"` - ErrMsg string `gorm:"column:err_msg"` - InstanceID string `gorm:"column:instance_id"` - LastFailed time.Time `gorm:"column:last_failed"` -} - -type failedEventSearchRequest struct { - Offset uint64 - Limit uint64 - SortingColumn failedEventSearchKey - Asc bool - Queries []*FailedEventSearchQuery -} - -func (f failedEventSearchRequest) GetLimit() uint64 { - return f.Limit -} - -func (f failedEventSearchRequest) GetOffset() uint64 { - return f.Offset -} - -func (f failedEventSearchRequest) GetSortingColumn() ColumnKey { - if f.SortingColumn == failedEventSearchKey(FailedEventKeyUndefined) { - return nil - } - return f.SortingColumn -} - -func (f failedEventSearchRequest) GetAsc() bool { - return f.Asc -} - -func (f failedEventSearchRequest) GetQueries() []SearchQuery { - result := make([]SearchQuery, len(f.Queries)) - for i, q := range f.Queries { - result[i] = q - } - return result -} - -type FailedEventSearchQuery struct { - Key FailedEventSearchKey - Method domain.SearchMethod - Value interface{} -} - -func (req FailedEventSearchQuery) GetKey() ColumnKey { - return failedEventSearchKey(req.Key) -} - -func (req FailedEventSearchQuery) GetMethod() domain.SearchMethod { - return req.Method -} - -func (req FailedEventSearchQuery) GetValue() interface{} { - return req.Value -} - -type FailedEventSearchKey int32 - -const ( - FailedEventKeyUndefined FailedEventSearchKey = iota - FailedEventKeyViewName - FailedEventKeyFailedSequence - FailedEventKeyInstanceID - FailedEventKeyLastFailed -) - -type failedEventSearchKey FailedEventSearchKey - -func (key failedEventSearchKey) ToColumnName() string { - switch FailedEventSearchKey(key) { - case FailedEventKeyViewName: - return "view_name" - case FailedEventKeyFailedSequence: - return "failed_sequence" - case FailedEventKeyInstanceID: - return "instance_id" - case FailedEventKeyLastFailed: - return "last_failed" - default: - return "" - } -} - -func FailedEventFromModel(failedEvent *view_model.FailedEvent) *FailedEvent { - return &FailedEvent{ - ViewName: failedEvent.Database + "." + failedEvent.ViewName, - FailureCount: failedEvent.FailureCount, - FailedSequence: failedEvent.FailedSequence, - InstanceID: failedEvent.InstanceID, - ErrMsg: failedEvent.ErrMsg, - } -} -func FailedEventToModel(failedEvent *FailedEvent) *view_model.FailedEvent { - dbView := strings.Split(failedEvent.ViewName, ".") - return &view_model.FailedEvent{ - Database: dbView[0], - ViewName: dbView[1], - FailureCount: failedEvent.FailureCount, - FailedSequence: failedEvent.FailedSequence, - ErrMsg: failedEvent.ErrMsg, - LastFailed: failedEvent.LastFailed, - } -} - -func SaveFailedEvent(db *gorm.DB, table string, failedEvent *FailedEvent) error { - save := PrepareSave(table) - err := save(db, failedEvent) - - if err != nil { - return errors.ThrowInternal(err, "VIEW-4F8us", "unable to updated failed events") - } - return nil -} - -func RemoveFailedEvent(db *gorm.DB, table string, failedEvent *FailedEvent) error { - delete := PrepareDeleteByKeys(table, - Key{Key: failedEventSearchKey(FailedEventKeyViewName), Value: failedEvent.ViewName}, - Key{Key: failedEventSearchKey(FailedEventKeyFailedSequence), Value: failedEvent.FailedSequence}, - Key{Key: failedEventSearchKey(FailedEventKeyInstanceID), Value: failedEvent.InstanceID}, - ) - return delete(db) -} - -func LatestFailedEvent(db *gorm.DB, table, viewName, instanceID string, sequence uint64) (*FailedEvent, error) { - failedEvent := new(FailedEvent) - queries := []SearchQuery{ - FailedEventSearchQuery{Key: FailedEventKeyViewName, Method: domain.SearchMethodEqualsIgnoreCase, Value: viewName}, - FailedEventSearchQuery{Key: FailedEventKeyFailedSequence, Method: domain.SearchMethodEquals, Value: sequence}, - FailedEventSearchQuery{Key: FailedEventKeyInstanceID, Method: domain.SearchMethodEquals, Value: instanceID}, - } - query := PrepareGetByQuery(table, queries...) - err := query(db, failedEvent) - - if err == nil && failedEvent.ViewName != "" { - return failedEvent, nil - } - - if errors.IsNotFound(err) { - return &FailedEvent{ - ViewName: viewName, - FailedSequence: sequence, - FailureCount: 0, - }, nil - } - return nil, errors.ThrowInternalf(err, "VIEW-9LyCB", "unable to get failed events of %s", viewName) - -} - -func AllFailedEvents(db *gorm.DB, table, instanceID string) ([]*FailedEvent, error) { - queries := make([]*FailedEventSearchQuery, 0, 1) - if instanceID != "" { - queries = append(queries, &FailedEventSearchQuery{Key: FailedEventKeyInstanceID, Method: domain.SearchMethodEquals, Value: instanceID}) - } - failedEvents := make([]*FailedEvent, 0) - query := PrepareSearchQuery(table, &failedEventSearchRequest{SortingColumn: failedEventSearchKey(FailedEventKeyLastFailed), Queries: queries}) - _, err := query(db, &failedEvents) - if err != nil { - return nil, err - } - return failedEvents, nil -} diff --git a/internal/view/repository/general_query.go b/internal/view/repository/general_query.go deleted file mode 100644 index 6b27d3d79..000000000 --- a/internal/view/repository/general_query.go +++ /dev/null @@ -1,53 +0,0 @@ -package repository - -import ( - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/view/model" -) - -type GeneralSearchRequest model.GeneralSearchRequest -type GeneralSearchQuery model.GeneralSearchQuery -type GeneralSearchKey model.GeneralSearchKey - -func (req GeneralSearchRequest) GetLimit() uint64 { - return req.Limit -} - -func (req GeneralSearchRequest) GetOffset() uint64 { - return req.Offset -} - -func (req GeneralSearchRequest) GetSortingColumn() ColumnKey { - if req.SortingColumn == model.GeneralSearchKeyUnspecified { - return nil - } - return GeneralSearchKey(req.SortingColumn) -} - -func (req GeneralSearchRequest) GetAsc() bool { - return req.Asc -} - -func (req GeneralSearchRequest) GetQueries() []SearchQuery { - result := make([]SearchQuery, len(req.Queries)) - for i, q := range req.Queries { - result[i] = GeneralSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} - } - return result -} - -func (req GeneralSearchQuery) GetKey() ColumnKey { - return GeneralSearchKey(req.Key) -} - -func (req GeneralSearchQuery) GetMethod() domain.SearchMethod { - return req.Method -} - -func (req GeneralSearchQuery) GetValue() interface{} { - return req.Value -} - -func (key GeneralSearchKey) ToColumnName() string { - return "" -} diff --git a/internal/view/repository/query.go b/internal/view/repository/query.go index 77d27c75b..ba18c71ba 100644 --- a/internal/view/repository/query.go +++ b/internal/view/repository/query.go @@ -139,7 +139,7 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method domain.Se if !ok { return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-Psois", "list contains only possible for strings") } - query = query.Where("? <@ "+column, database.StringArray{valueText}) + query = query.Where("? <@ "+column, database.TextArray[string]{valueText}) default: return nil, nil } diff --git a/internal/view/repository/requests.go b/internal/view/repository/requests.go index 74921fbcb..9de4f9fcd 100644 --- a/internal/view/repository/requests.go +++ b/internal/view/repository/requests.go @@ -13,30 +13,6 @@ import ( caos_errs "github.com/zitadel/zitadel/internal/errors" ) -func PrepareGetByKey(table string, key ColumnKey, id string) func(db *gorm.DB, res interface{}) error { - return func(db *gorm.DB, res interface{}) error { - tx := db.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}) - defer func() { - if err := tx.Commit().Error; err != nil { - logging.OnError(err).Info("commit failed") - } - }() - - err := tx.Table(table). - Where(fmt.Sprintf("%s = ?", key.ToColumnName()), id). - Take(res). - Error - if err == nil { - return nil - } - if errors.Is(err, gorm.ErrRecordNotFound) { - return caos_errs.ThrowNotFound(err, "VIEW-XRI9c", "object not found") - } - logging.LogWithFields("VIEW-xVShS", "AggregateID", id).WithError(err).Warn("get from view error") - return caos_errs.ThrowInternal(err, "VIEW-J92Td", "Errors.Internal") - } -} - func PrepareGetByQuery(table string, queries ...SearchQuery) func(db *gorm.DB, res interface{}) error { return func(db *gorm.DB, res interface{}) error { query := db.Table(table) @@ -161,27 +137,3 @@ func PrepareDeleteByKeys(table string, keys ...Key) func(db *gorm.DB) error { return nil } } - -func PrepareDeleteByObject(table string, object interface{}) func(db *gorm.DB) error { - return func(db *gorm.DB) error { - err := db.Table(table). - Delete(object). - Error - if err != nil { - return caos_errs.ThrowInternal(err, "VIEW-lso9w", "could not delete object") - } - return nil - } -} - -func PrepareTruncate(table string) func(db *gorm.DB) error { - return func(db *gorm.DB) error { - err := db. - Exec("TRUNCATE " + table). - Error - if err != nil { - return caos_errs.ThrowInternal(err, "VIEW-lso9w", "could not truncate table") - } - return nil - } -} diff --git a/internal/view/repository/requests_test.go b/internal/view/repository/requests_test.go index 46c4e1532..c852d15a7 100644 --- a/internal/view/repository/requests_test.go +++ b/internal/view/repository/requests_test.go @@ -9,90 +9,6 @@ import ( caos_errs "github.com/zitadel/zitadel/internal/errors" ) -func TestPrepareGetByKey(t *testing.T) { - type args struct { - table string - key ColumnKey - value string - } - type res struct { - result Test - wantErr bool - errFunc func(err error) bool - } - tests := []struct { - name string - db *dbMock - args args - res res - }{ - { - "ok", - mockDB(t). - expectGetByID("TESTTABLE", "test", "VALUE"), - args{ - table: "TESTTABLE", - key: TestSearchKey_TEST, - value: "VALUE", - }, - res{ - result: Test{ID: "VALUE"}, - wantErr: false, - }, - }, - { - "not found", - mockDB(t). - expectGetByIDErr("TESTTABLE", "test", "VALUE", gorm.ErrRecordNotFound), - args{ - table: "TESTTABLE", - key: TestSearchKey_TEST, - value: "VALUE", - }, - res{ - result: Test{ID: "VALUE"}, - wantErr: true, - errFunc: caos_errs.IsNotFound, - }, - }, - { - "db err", - mockDB(t). - expectGetByIDErr("TESTTABLE", "test", "VALUE", gorm.ErrUnaddressable), - args{ - table: "TESTTABLE", - key: TestSearchKey_TEST, - value: "VALUE", - }, - res{ - result: Test{ID: "VALUE"}, - wantErr: true, - errFunc: caos_errs.IsInternal, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res := &Test{} - getByID := PrepareGetByKey(tt.args.table, tt.args.key, tt.args.value) - err := getByID(tt.db.db, res) - - if !tt.res.wantErr && err != nil { - t.Errorf("got wrong err should be nil: %v ", err) - } - - if tt.res.wantErr && !tt.res.errFunc(err) { - t.Errorf("got wrong err: %v ", err) - } - if err := tt.db.mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } - - tt.db.close() - }) - } -} - func TestPrepareGetByQuery(t *testing.T) { type args struct { table string @@ -483,86 +399,3 @@ func TestPrepareDeleteByKeys(t *testing.T) { }) } } - -func TestPrepareDeleteByObject(t *testing.T) { - type args struct { - table string - object interface{} - } - type res struct { - result Test - wantErr bool - errFunc func(err error) bool - } - tests := []struct { - name string - db *dbMock - args args - res res - }{ - { - "delete", - mockDB(t). - expectBegin(nil). - expectRemoveByObject("TESTTABLE", Test{ID: "VALUE", Test: "TEST"}). - expectCommit(nil), - args{ - table: "TESTTABLE", - object: &Test{ID: "VALUE", Test: "TEST"}, - }, - res{ - result: Test{ID: "VALUE"}, - wantErr: false, - }, - }, - { - "delete multiple PK", - mockDB(t). - expectBegin(nil). - expectRemoveByObjectMultiplePKs("TESTTABLE", TestMultiplePK{TestID: "TESTID", HodorID: "HODORID", Test: "TEST"}). - expectCommit(nil), - args{ - table: "TESTTABLE", - object: &TestMultiplePK{TestID: "TESTID", HodorID: "HODORID", Test: "TEST"}, - }, - res{ - wantErr: false, - }, - }, - { - "db error", - mockDB(t). - expectBegin(nil). - expectRemoveErr("TESTTABLE", "id", "VALUE", gorm.ErrUnaddressable). - expectCommit(nil), - args{ - table: "TESTTABLE", - object: &Test{ID: "VALUE", Test: "TEST"}, - }, - res{ - result: Test{ID: "VALUE"}, - wantErr: true, - errFunc: caos_errs.IsInternal, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - getDelete := PrepareDeleteByObject(tt.args.table, tt.args.object) - err := getDelete(tt.db.db) - - if !tt.res.wantErr && err != nil { - t.Errorf("got wrong err should be nil: %v ", err) - } - - if tt.res.wantErr && !tt.res.errFunc(err) { - t.Errorf("got wrong err: %v ", err) - } - if err := tt.db.mock.ExpectationsWereMet(); !tt.res.wantErr && err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } - - tt.db.close() - }) - } -} diff --git a/internal/view/repository/sequence.go b/internal/view/repository/sequence.go deleted file mode 100644 index 5ccb7a36f..000000000 --- a/internal/view/repository/sequence.go +++ /dev/null @@ -1,216 +0,0 @@ -package repository - -import ( - "strings" - "time" - - "github.com/jinzhu/gorm" - - "github.com/zitadel/zitadel/internal/domain" - caos_errs "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/view/model" -) - -type CurrentSequence struct { - ViewName string `gorm:"column:view_name;primary_key"` - CurrentSequence uint64 `gorm:"column:current_sequence"` - EventTimestamp time.Time `gorm:"column:event_timestamp"` - LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` - InstanceID string `gorm:"column:instance_id;primary_key"` -} - -type currentSequenceViewWithSequence struct { - ViewName string `gorm:"column:view_name;primary_key"` - CurrentSequence uint64 `gorm:"column:current_sequence"` - LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` -} - -type currentSequenceView struct { - ViewName string `gorm:"column:view_name;primary_key"` - LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` -} - -type SequenceSearchKey int32 - -const ( - SequenceSearchKeyUndefined SequenceSearchKey = iota - SequenceSearchKeyViewName - SequenceSearchKeyAggregateType - SequenceSearchKeyInstanceID -) - -type sequenceSearchKey SequenceSearchKey - -func (key sequenceSearchKey) ToColumnName() string { - switch SequenceSearchKey(key) { - case SequenceSearchKeyViewName: - return "view_name" - case SequenceSearchKeyAggregateType: - return "aggregate_type" - case SequenceSearchKeyInstanceID: - return "instance_id" - default: - return "" - } -} - -type sequenceSearchQuery struct { - key sequenceSearchKey - method domain.SearchMethod - value interface{} -} - -func (q *sequenceSearchQuery) GetKey() ColumnKey { - return q.key -} - -func (q *sequenceSearchQuery) GetMethod() domain.SearchMethod { - return q.method -} - -func (q *sequenceSearchQuery) GetValue() interface{} { - return q.value -} - -type sequenceSearchRequest struct { - queries []sequenceSearchQuery -} - -func (s *sequenceSearchRequest) GetLimit() uint64 { - return 0 -} - -func (s *sequenceSearchRequest) GetOffset() uint64 { - return 0 -} - -func (s *sequenceSearchRequest) GetSortingColumn() ColumnKey { - return nil -} - -func (s *sequenceSearchRequest) GetAsc() bool { - return false -} - -func (s *sequenceSearchRequest) GetQueries() []SearchQuery { - result := make([]SearchQuery, len(s.queries)) - for i, q := range s.queries { - result[i] = &sequenceSearchQuery{key: q.key, value: q.value, method: q.method} - } - return result -} - -func CurrentSequenceToModel(sequence *CurrentSequence) *model.View { - dbView := strings.Split(sequence.ViewName, ".") - return &model.View{ - Database: dbView[0], - ViewName: dbView[1], - CurrentSequence: sequence.CurrentSequence, - EventTimestamp: sequence.EventTimestamp, - LastSuccessfulSpoolerRun: sequence.LastSuccessfulSpoolerRun, - } -} - -func SaveCurrentSequence(db *gorm.DB, table, viewName, instanceID string, sequence uint64, eventTimestamp time.Time) error { - return UpdateCurrentSequence(db, table, &CurrentSequence{viewName, sequence, eventTimestamp, time.Now(), instanceID}) -} - -func SaveCurrentSequences(db *gorm.DB, table, viewName string, sequence uint64, eventTimestamp time.Time) error { - err := db.Table(table).Where("view_name = ?", viewName). - Updates(map[string]interface{}{"current_sequence": sequence, "event_timestamp": eventTimestamp, "last_successful_spooler_run": time.Now()}).Error - if err != nil { - return caos_errs.ThrowInternal(err, "VIEW-Sfdqs", "unable to updated processed sequence") - } - return nil -} - -func UpdateCurrentSequence(db *gorm.DB, table string, currentSequence *CurrentSequence) (err error) { - save := PrepareSave(table) - err = save(db, currentSequence) - if err != nil { - return caos_errs.ThrowInternal(err, "VIEW-5kOhP", "unable to updated processed sequence") - } - return nil -} - -func UpdateCurrentSequences(db *gorm.DB, table string, currentSequences []*CurrentSequence) (err error) { - save := PrepareBulkSave(table) - s := make([]interface{}, len(currentSequences)) - for i, currentSequence := range currentSequences { - s[i] = currentSequence - } - err = save(db, s...) - if err != nil { - return caos_errs.ThrowInternal(err, "VIEW-5kOhP", "unable to updated processed sequence") - } - return nil -} - -func LatestSequence(db *gorm.DB, table, viewName, instanceID string) (*CurrentSequence, error) { - searchQueries := []SearchQuery{ - &sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyViewName), value: viewName, method: domain.SearchMethodEquals}, - &sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyInstanceID), value: instanceID, method: domain.SearchMethodIsOneOf}, - } - - // ensure highest sequence of view - db = db.Order("current_sequence DESC") - - query := PrepareGetByQuery(table, searchQueries...) - sequence := new(CurrentSequence) - err := query(db, sequence) - - if err == nil { - return sequence, nil - } - - if caos_errs.IsNotFound(err) { - return sequence, nil - } - return nil, caos_errs.ThrowInternalf(err, "VIEW-9LyCB", "unable to get latest sequence of %s", viewName) -} - -func LatestSequences(db *gorm.DB, table, viewName string, instanceIDs []string) ([]*CurrentSequence, error) { - searchQueries := []sequenceSearchQuery{ - {key: sequenceSearchKey(SequenceSearchKeyViewName), value: viewName, method: domain.SearchMethodEquals}, - } - if len(instanceIDs) > 0 { - searchQueries = append(searchQueries, sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyInstanceID), value: instanceIDs, method: domain.SearchMethodIsOneOf}) - } - searchRequest := &sequenceSearchRequest{ - queries: searchQueries, - } - - // ensure highest sequence of view - db = db.Order("current_sequence DESC") - - sequences := make([]*CurrentSequence, 0) - query := PrepareSearchQuery(table, searchRequest) - _, err := query(db, &sequences) - if err != nil { - return nil, err - } - return sequences, nil -} - -func AllCurrentSequences(db *gorm.DB, table, instanceID string) ([]*CurrentSequence, error) { - queries := make([]sequenceSearchQuery, 0, 1) - if instanceID != "" { - queries = append(queries, sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyInstanceID), value: instanceID}) - } - sequences := make([]*CurrentSequence, 0) - query := PrepareSearchQuery(table, &sequenceSearchRequest{queries: queries}) - _, err := query(db, &sequences) - if err != nil { - return nil, err - } - return sequences, nil -} - -func ClearView(db *gorm.DB, truncateView, sequenceTable string) error { - truncate := PrepareTruncate(truncateView) - err := truncate(db) - if err != nil { - return err - } - return SaveCurrentSequences(db, sequenceTable, truncateView, 0, time.Now()) -} From bd23a7a56f3267af59365f7b4586a7d549da6a5f Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Thu, 19 Oct 2023 12:34:00 +0200 Subject: [PATCH 76/76] merge main into next --- .github/workflows/console.yml | 1 + .github/workflows/core-integration-test.yml | 1 + .github/workflows/core-unit-test.yml | 1 + .github/workflows/core.yml | 1 + Makefile | 2 +- build/workflow.Dockerfile | 4 +- cmd/defaults.yaml | 2 +- cmd/setup/steps.yaml | 4 +- cmd/start/start.go | 2 +- .../auth-user-mfa/auth-user-mfa.component.ts | 4 +- .../user-mfa/user-mfa.component.ts | 30 ++++ console/src/app/services/mgmt.service.ts | 16 ++ docs/docs/apis/actions/modules.md | 55 ++++++ docs/docs/legal/onboarding-support.md | 87 ++++++++++ docs/docs/legal/support-services.md | 5 +- docs/docs/support/trainings/application.md | 36 ---- docs/docs/support/trainings/introduction.md | 30 ---- docs/docs/support/trainings/project.md | 35 ---- docs/docs/support/trainings/recurring.md | 33 ---- docs/sidebars.js | 17 +- go.mod | 30 ++-- go.sum | 53 +++--- internal/actions/uuid_module.go | 83 +++++++++ internal/api/authz/token.go | 6 +- internal/api/grpc/management/information.go | 2 +- internal/api/grpc/management/user.go | 22 ++- internal/api/grpc/oidc/v2/oidc.go | 2 +- .../api/grpc/oidc/v2/oidc_integration_test.go | 12 +- internal/api/grpc/oidc/v2/server.go | 2 +- .../server/middleware/auth_interceptor.go | 40 ++++- internal/api/grpc/session/v2/session.go | 57 ++++++- .../session/v2/session_integration_test.go | 84 ++++++--- internal/api/grpc/session/v2/session_test.go | 161 +++++++++++++++++- .../api/grpc/system/instance_converter.go | 2 +- internal/api/oidc/auth_request.go | 4 +- internal/api/oidc/auth_request_converter.go | 4 +- .../api/oidc/auth_request_converter_v2.go | 2 +- .../api/oidc/auth_request_integration_test.go | 91 +++++----- internal/api/oidc/client.go | 10 +- internal/api/oidc/client_converter.go | 4 +- internal/api/oidc/client_credentials.go | 4 +- internal/api/oidc/client_integration_test.go | 14 +- internal/api/oidc/device_auth.go | 4 +- internal/api/oidc/jwt-profile.go | 4 +- internal/api/oidc/key.go | 4 +- internal/api/oidc/oidc_integration_test.go | 12 +- internal/api/oidc/op.go | 4 +- internal/api/saml/certificate.go | 2 +- internal/api/ui/console/console.go | 2 +- internal/api/ui/login/custom_action.go | 10 +- .../api/ui/login/external_provider_handler.go | 4 +- internal/api/ui/login/jwt_handler.go | 2 +- .../eventstore/token_verifier.go | 8 +- internal/command/auth_request_test.go | 44 ++++- internal/command/idp_intent.go | 2 +- internal/command/idp_intent_test.go | 2 +- internal/command/idp_model.go | 2 +- internal/command/instance_idp_test.go | 2 +- internal/command/oidc_session_test.go | 22 ++- internal/command/org_idp_test.go | 2 +- internal/command/org_test.go | 2 +- internal/command/session.go | 8 +- internal/command/session_test.go | 88 ++++++++-- .../command/user_human_refresh_token_test.go | 2 +- internal/crypto/passwap.go | 2 +- internal/crypto/passwap_test.go | 5 +- internal/domain/user_agent.go | 17 ++ internal/idp/providers/apple/apple.go | 6 +- internal/idp/providers/apple/session.go | 2 +- internal/idp/providers/apple/session_test.go | 2 +- internal/idp/providers/azuread/azuread.go | 2 +- .../idp/providers/azuread/azuread_test.go | 4 +- internal/idp/providers/azuread/session.go | 4 +- .../idp/providers/azuread/session_test.go | 2 +- internal/idp/providers/github/session_test.go | 2 +- internal/idp/providers/gitlab/gitlab.go | 2 +- internal/idp/providers/gitlab/session_test.go | 4 +- internal/idp/providers/google/google.go | 2 +- internal/idp/providers/google/session_test.go | 4 +- internal/idp/providers/jwt/session.go | 4 +- internal/idp/providers/jwt/session_test.go | 4 +- internal/idp/providers/oauth/oauth2.go | 4 +- internal/idp/providers/oauth/oauth2_test.go | 2 +- internal/idp/providers/oauth/session.go | 6 +- internal/idp/providers/oauth/session_test.go | 2 +- internal/idp/providers/oidc/oidc.go | 6 +- internal/idp/providers/oidc/oidc_test.go | 4 +- internal/idp/providers/oidc/session.go | 6 +- internal/idp/providers/oidc/session_test.go | 6 +- internal/integration/client.go | 2 +- internal/integration/integration.go | 4 +- internal/integration/oidc.go | 24 +-- .../protoc-gen-zitadel/zitadel.pb.go.tmpl | 4 +- internal/query/prepare_test.go | 12 +- internal/query/projection/session.go | 94 ++++++---- internal/query/projection/session_test.go | 38 +++-- internal/query/session.go | 33 ++++ internal/query/sessions_test.go | 107 +++++++----- internal/repository/session/session.go | 3 + .../v2beta/user_service_org.pb.zitadel.go | 12 ++ proto/zitadel/admin.proto | 12 +- proto/zitadel/management.proto | 94 +++++++++- proto/zitadel/object/v2beta/object.proto | 8 + proto/zitadel/project.proto | 2 +- proto/zitadel/session/v2beta/session.proto | 24 ++- .../session/v2beta/session_service.proto | 1 + proto/zitadel/user/v2beta/user_service.proto | 10 +- 107 files changed, 1321 insertions(+), 554 deletions(-) create mode 100644 docs/docs/legal/onboarding-support.md delete mode 100644 docs/docs/support/trainings/application.md delete mode 100644 docs/docs/support/trainings/introduction.md delete mode 100644 docs/docs/support/trainings/project.md delete mode 100644 docs/docs/support/trainings/recurring.md create mode 100644 internal/actions/uuid_module.go create mode 100644 internal/domain/user_agent.go create mode 100644 pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go diff --git a/.github/workflows/console.yml b/.github/workflows/console.yml index bc4c5f089..8471bf5af 100644 --- a/.github/workflows/console.yml +++ b/.github/workflows/console.yml @@ -30,6 +30,7 @@ jobs: - uses: actions/cache/restore@v3 timeout-minutes: 1 + continue-on-error: true id: cache with: key: console-${{ hashFiles('console', 'proto', '!console/dist') }} diff --git a/.github/workflows/core-integration-test.yml b/.github/workflows/core-integration-test.yml index c99987d25..f57a79767 100644 --- a/.github/workflows/core-integration-test.yml +++ b/.github/workflows/core-integration-test.yml @@ -56,6 +56,7 @@ jobs: uses: actions/cache/restore@v3 id: cache timeout-minutes: 1 + continue-on-error: true name: restore previous results with: key: integration-test-postgres-${{ inputs.core_cache_key }} diff --git a/.github/workflows/core-unit-test.yml b/.github/workflows/core-unit-test.yml index c831758d4..3097cfd57 100644 --- a/.github/workflows/core-unit-test.yml +++ b/.github/workflows/core-unit-test.yml @@ -40,6 +40,7 @@ jobs: uses: actions/cache/restore@v3 id: cache timeout-minutes: 1 + continue-on-error: true name: restore previous results with: key: unit-test-${{ inputs.core_cache_key }} diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index c5d868533..45c4bbda0 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -41,6 +41,7 @@ jobs: - uses: actions/cache/restore@v3 timeout-minutes: 1 + continue-on-error: true id: cache with: key: core-${{ hashFiles( 'go.*', 'openapi', 'cmd', 'pkg/grpc/**/*.go', 'proto', 'internal') }} diff --git a/Makefile b/Makefile index 6495993ed..dd07fe49c 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ console_build: console_dependencies console_client .PHONY: clean clean: - $(RM) .artifacts/grpc + $(RM) -r .artifacts/grpc $(RM) $(gen_authopt_path) $(RM) $(gen_zitadel_path) diff --git a/build/workflow.Dockerfile b/build/workflow.Dockerfile index c9fb7e6c2..db27daf91 100644 --- a/build/workflow.Dockerfile +++ b/build/workflow.Dockerfile @@ -103,7 +103,7 @@ COPY --from=core-assets /go/src/github.com/zitadel/zitadel/internal ./internal # ####################################### # download console dependencies # ####################################### -FROM node:18-buster AS console-deps +FROM node:20-buster AS console-deps WORKDIR /zitadel/console @@ -115,7 +115,7 @@ RUN yarn install --frozen-lockfile # ####################################### # generate console client # ####################################### -FROM node:18-buster AS console-client +FROM node:20-buster AS console-client WORKDIR /zitadel/console diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 7897e1283..a681bedca 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -767,7 +767,7 @@ DefaultInstance: PreHeader: Verify email Subject: Verify email Greeting: Hello {{.DisplayName}}, - Text: A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you din't add a new email, please ignore this email. + Text: A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you din't add a new email, please ignore this email. ButtonText: Verify email - MessageTextType: VerifyPhone Language: en diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index 1497b7be4..c585c535b 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -35,8 +35,8 @@ FirstInstance: # If FirstInstance.Org.Machine.Machine is defined, a service user is created with the IAM_OWNER role, not a human user. Machine: Machine: - Username: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_USERNAME - Name: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_NAME + Username: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME + Name: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME MachineKey: # date format: 2023-01-01T00:00:00Z ExpirationDate: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_EXPIRATIONDATE diff --git a/cmd/start/start.go b/cmd/start/start.go index dfc759e3a..744e1f80a 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -19,7 +19,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/saml/pkg/provider" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index 6a81838c4..c4a3d6403 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -157,7 +157,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { this.service .removeMyAuthFactorOTPEmail() .then(() => { - this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); this.cleanupList(); this.getMFAs(); @@ -169,7 +169,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { this.service .removeMyAuthFactorOTPSMS() .then(() => { - this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); this.cleanupList(); this.getMFAs(); diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index c9be008e0..29418e80f 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -102,6 +102,36 @@ export class UserMfaComponent implements OnInit, OnDestroy { .catch((error) => { this.toast.showError(error); }); + } else if (factor.otpEmail) { + this.mgmtUserService + .removeHumanAuthFactorOTPEmail(this.user.id) + .then(() => { + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); + + const index = this.dataSource.data.findIndex((mfa) => !!mfa.otpEmail); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); + }) + .catch((error) => { + this.toast.showError(error); + }); + } else if (factor.otpSms) { + this.mgmtUserService + .removeHumanAuthFactorOTPSMS(this.user.id) + .then(() => { + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); + + const index = this.dataSource.data.findIndex((mfa) => !!mfa.otpSms); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); + }) + .catch((error) => { + this.toast.showError(error); + }); } } }); diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index c80e9e9b4..f2389a25e 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -322,8 +322,12 @@ import { RemoveCustomLabelPolicyLogoDarkResponse, RemoveCustomLabelPolicyLogoRequest, RemoveCustomLabelPolicyLogoResponse, + RemoveHumanAuthFactorOTPEmailRequest, + RemoveHumanAuthFactorOTPEmailResponse, RemoveHumanAuthFactorOTPRequest, RemoveHumanAuthFactorOTPResponse, + RemoveHumanAuthFactorOTPSMSRequest, + RemoveHumanAuthFactorOTPSMSResponse, RemoveHumanAuthFactorU2FRequest, RemoveHumanAuthFactorU2FResponse, RemoveHumanLinkedIDPRequest, @@ -1805,6 +1809,18 @@ export class ManagementService { return this.grpcService.mgmt.removeHumanAuthFactorU2F(req, null).then((resp) => resp.toObject()); } + public removeHumanAuthFactorOTPSMS(userId: string): Promise { + const req = new RemoveHumanAuthFactorOTPSMSRequest(); + req.setUserId(userId); + return this.grpcService.mgmt.removeHumanAuthFactorOTPSMS(req, null).then((resp) => resp.toObject()); + } + + public removeHumanAuthFactorOTPEmail(userId: string): Promise { + const req = new RemoveHumanAuthFactorOTPEmailRequest(); + req.setUserId(userId); + return this.grpcService.mgmt.removeHumanAuthFactorOTPEmail(req, null).then((resp) => resp.toObject()); + } + public updateHumanProfile( userId: string, firstName?: string, diff --git a/docs/docs/apis/actions/modules.md b/docs/docs/apis/actions/modules.md index 2cc99a222..6624ff9de 100644 --- a/docs/docs/apis/actions/modules.md +++ b/docs/docs/apis/actions/modules.md @@ -51,3 +51,58 @@ The object has the following fields and methods: Returns the body as JSON object, or throws an error if the body is not a json object. - `text()` *string* Returns the body + +## UUID + +This module provides functionality to generate a UUID + +### Import + +```js + let uuid = require("zitadel/uuid") +``` + +### `uuid.vX()` function + +This function generates a UUID using [google/uuid](https://github.com/google/uuid). `vX` allows to define the UUID version: + +- `uuid.v1()` *string* + Generates a UUID version 1, based on date-time and MAC address +- `uuid.v3(namespace, data)` *string* + Generates a UUID version 3, based on the provided namespace using MD5 +- `uuid.v4()` *string* + Generates a UUID version 4, which is randomly generated +- `uuid.v5(namespace, data)` *string* + Generates a UUID version 5, based on the provided namespace using SHA1 + +#### Parameters + +- `namespace` *UUID*/*string* + Namespace to be used in the hashing function. Either provide one of defined [namespaces](#namespaces) or a string representing a UUID. +- `data` *[]byte*/*string* + data to be used in the hashing function. Possible types are []byte or string. + +### Namespaces + +The following predefined namespaces can be used for `uuid.v3` and `uuid.v5`: + +- `uuid.namespaceDNS` *UUID* + 6ba7b810-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceURL` *UUID* + 6ba7b811-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceOID` *UUID* + 6ba7b812-9dad-11d1-80b4-00c04fd430c8 +- `uuid.namespaceX500` *UUID* + 6ba7b814-9dad-11d1-80b4-00c04fd430c8 + +### Example +```js +let uuid = require("zitadel/uuid") +function setUUID(ctx, api) { + if (api.metadata === undefined) { + return; + } + + api.v1.user.appendMetadata('custom-id', uuid.v4()); +} +``` \ No newline at end of file diff --git a/docs/docs/legal/onboarding-support.md b/docs/docs/legal/onboarding-support.md new file mode 100644 index 000000000..e9c76ac38 --- /dev/null +++ b/docs/docs/legal/onboarding-support.md @@ -0,0 +1,87 @@ +--- +title: Description of onboarding support services for ZITADEL +sidebar_label: Onboarding support +custom_edit_url: null +--- + +This annex of the [Framework Agreement](terms-of-service) describes the onboarding support services offered by us for our services. + +Last revised: October 12, 2023 + +Our onboarding support should help you, as a new customer, to get a better understanding on how to integrate ZITADEL into your solution, how to tackle the migration, and ensure a highly-available day-to-day operation. + +Onboarding support services can be offered to customers that enter a ZITADEL Cloud or a ZITADEL Enterprise subscription. + +If you intend to use the open source version exclusively then please join our community chat or Github. +Your questions might help other people in the community and will make our project better over time. + +Please [contact us](https://zitadel.com/contact) for a quote and to get started with onboarding support. +Below you will find topics covered and scope of the offered services. + +## Proof of value + +Within a short time-frame, f.e. 3 weeks, we can show the value of using our services and have the ability to establish the proof a of working setup for your most critical use cases. +We may offer to support you during an initial period to evaluate next steps. +Before the start of the period we may ask you to provide a description of your critical use cases and a high-level overview of your planned integration architecture. +During this period you should make sure that you have the necessary resources on your side to complete the proof of value. + +## Onboarding term + +With the onboarding support we provide the initial knowledge transfer to configure and operate ZITADEL. +During the term you will get direct access to our engineering team via [Technical Account Management](./support-services.md#technical-account-manager). +Duration is typically 3 months but this could vary depending on your requirements. + +We offer an onboarding term in combination with ZITADEL Enterprise subscriptions. + +### Topics covered + +Topics of the onboarding term may include: + +- Administration +- DevOps (Operation) +- Architecture +- Integration +- Migration +- Security Best Practices & Go-Live Checkup + +The scope will be tailored to your requirements. + +More details + +- IAM Configuration +- Walk-though all features +- Users / Manuals +- Authentication & Management APIs +- Validation of tokens +- Client integration best-practices +- Event types +- Database schemas and compute models +- Accessing database +- Observability (Logs, Errors, Metrics, Tracing) +- Operations best practices (Deployment, Backup, Networking etc.) +- Check prerequisites and architecture +- Troubleshoot installation and configuration of ZITADEL +- Troubleshoot and configuration connectivity to the database +- Functional testing of the ZITADEL instance + +
+ Out of scope +
    +
  • Performance testing
  • +
  • Setting up or maintaining backup storage
  • +
  • Running multiple ZITADEL instances on the same cluster
  • +
  • Integration into internal monitoring and alerting
  • +
  • Multi-cluster architecture deployments
  • +
  • DNS, Network and Firewall configuration
  • +
  • Customer-specific Kubernetes configuration needs
  • +
  • Non-production environments
  • +
  • Production deployment
  • +
  • Application-side coding, configuration, or tuning
  • + +
+
+ +## Continuous support + +After the onboarding phase has ended we will provide continuous support according to your subscription. +We can provide you with continued access to the technical account management in our Enterprise subscriptions. diff --git a/docs/docs/legal/support-services.md b/docs/docs/legal/support-services.md index 1c0f36b3f..1a6f0a155 100644 --- a/docs/docs/legal/support-services.md +++ b/docs/docs/legal/support-services.md @@ -10,7 +10,7 @@ This annex of the [Framework Agreement](terms-of-service) and the [Support Servi Support Services for products and services provided by ZITADEL is offered to customers according to the terms and conditions outlined in this document. The customer may purchase support services from ZITADEL (CAOS Ltd.) directly. -Last revised: March 15, 2023 +Last revised: October 6, 2023 ## Support Services @@ -82,7 +82,8 @@ Phone Support | +41 43 215 27 34 ZITADEL will enhance its support offering by providing eligible clients with a Technical Account Manager (TAM), who will perform the following tasks for up to the specified amount of time per week during the term of service: - Provide support and advice regarding best practices on platform, product and configuration covered by the applicable Support Services; -- Participate in review calls every other week at mutually agreed times addressing customer’s operational issues. +- Participate in review calls every other week at mutually agreed times addressing customer’s operational challenges or complex support requests; +- Walk-through of new features and customer feedback. We offer TAM services only bundled with specific subscription plans, and the option to add more TAM hours to these plans. If you require consulting for your projects, please request a quote via our [website](https://zitadel.com/contact). diff --git a/docs/docs/support/trainings/application.md b/docs/docs/support/trainings/application.md deleted file mode 100644 index cf313bb96..000000000 --- a/docs/docs/support/trainings/application.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Application Support Trainings ---- - -## ZITADEL DevOps - -In this session your second level support and operations team will gain an understanding on how to extract relevant information for technical support questions and root cause analysis. We will also present our DevOps best practices and answer your questions. - -**Audience**: 2nd Level Support Staff, Operations -**Duration**: 0.5 day - -**Topics covered**: - -- Event types -- Database schemas and compute models -- Accessing database -- Observability (Logs, Errors, Metrics, Tracing) -- Operations best practices (Deployment, Backup, Networking etc.) -- Q&A - -## ZITADEL Administrator - -In this hands-on training your employees will get a complete overview of the system and learn how to configure and use ZITADEL. Your support staff will gain the required knowledge to provide user-support, while your solution owners gain an understanding about integration of clients. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 days - -**Topics covered**: - -- IAM Configuration -- Walk-though all features -- Users / Manuals -- APIs -- Validation of tokens -- Client integration best-practices -- Q&A diff --git a/docs/docs/support/trainings/introduction.md b/docs/docs/support/trainings/introduction.md deleted file mode 100644 index c597470f3..000000000 --- a/docs/docs/support/trainings/introduction.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: ZITADEL Trainings -sidebar_label: Introduction ---- - -The following pages describe the the trainings provided by ZITADEL. These trainings are intended for onboarding and during the course of a Support Program. - -Training should be held as block-sessions with the relevant staff from your organization. - -## Onboarding Project - -You receive professional onboarding support from our engineers, who help you to setup and configure ZITADEL on your infrastructure. - -[More information](project) - -## Application Support Trainings - -With the application support trainings we provide the initial knowledge transfer to manage and support ZITADEL. The trainings are held as block-sessions with relevant staff from your organization. Prices are flat-fee, excl. expenses. - -* [ZITADEL DevOps](application#zitadel-devops) -* [ZITADEL Administrator](application#zitadel-administrator) - -## Recurring Trainings - -While you can benefit from a technical account manager during your term, these trainings are designed to onboard new staff or update staff about larger changes to the platform. Prices are flat-fee, excl. expenses. - -* [ZITADEL Support Refresher](recurring#zitadel-support-refresher) -* [ZITADEL Support Onboarding](recurring#zitadel-support-onboarding) - -In case you have any questions please [get in touch](https://zitadel.com/contact). diff --git a/docs/docs/support/trainings/project.md b/docs/docs/support/trainings/project.md deleted file mode 100644 index 35f3259b4..000000000 --- a/docs/docs/support/trainings/project.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Onboarding Project ---- - -Effort required during depends on the complexity of your infrastructure and the overall setup. With a Multi-Zone Setup (excl. Multi-Region), support during this phase requires around 10-25h over 2 weeks. Actual effort is based on time and material. - -Scope of the project is agreed on individual basis. - -## In Scope - -- Check prerequisites and architecture -- Troubleshoot installation and configuration of ZITADEL -- Troubleshoot and configuration connectivity to the database -- Functional testing of the ZITADEL instance - -## Out of Scope - -- Running multiple ZITADEL instances on the same cluster -- Integration into internal monitoring and alerting -- Multi-cluster architecture deployments -- DNS, Network and Firewall configuration -- Customer-specific Kubernetes configuration needs -- Changes for specific environments -- Performance testing -- Production deployment -- Application-side coding, configuration, or tuning -- Changes or configuration on assets used in ZITADEL -- Setting up or maintaining backup storage - -## Prerequisites - -- Running Kubernetes with possibility to deploy to namespaces -- Inbound and outbound HTTP/2 traffic possible -- For being able to send SMS, we need a Twilio sender name, SID and an auth token -- ZITADEL also needs to connect to an email relay of your choice. We need the SMTP host, user and app key as well as the ZITADEL emails sender address and name. diff --git a/docs/docs/support/trainings/recurring.md b/docs/docs/support/trainings/recurring.md deleted file mode 100644 index 29982cf62..000000000 --- a/docs/docs/support/trainings/recurring.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Recurring Trainings ---- - -## ZITADEL Support Refresher - -In this session you can refresh knowledge about existing and gain experience with new features of ZITADEL to keep the quality of your support high. We recommend an half day training per support staff. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 day / support staff - -**Topics covered**: - -* Walk-through new features -* Review of difficult support issues -* Review of customer feedback -* Q&A - -## ZITADEL Support Onboarding - -In this hands-on training new support staff will get an overview of the system and learn how to configure and use ZITADEL to provide support for users. - -**Audience**: 1st / 2nd Level Support Staff, Solution Owner, QA Manager (optional) -**Duration**: 0.5 days / support staff - -**Topics covered**: - -* Event types -* Accessing database -* Logs and Errors -* Validation of tokens -* Walk-through key features -* Q&A diff --git a/docs/sidebars.js b/docs/sidebars.js index 1c82d8285..a2ba179c8 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -425,6 +425,11 @@ module.exports = { items: [ "support/software-release-cycles-support", "support/troubleshooting", + { + type: 'link', + label: 'Support Service Descriptions', + href: '/legal/support-services', + }, { type: 'category', label: "Technical Advisory", @@ -440,17 +445,6 @@ module.exports = { }, ], }, - { - type: "category", - label: "Trainings", - collapsed: true, - items: [ - "support/trainings/introduction", - "support/trainings/application", - "support/trainings/recurring", - "support/trainings/project", - ], - }, ] }, ], @@ -697,6 +691,7 @@ module.exports = { "legal/cloud-service-description", "legal/service-level-description", "legal/support-services", + "legal/onboarding-support", ], }, { diff --git a/go.mod b/go.mod index f4e513fbb..875f6bb0e 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/drone/envsubst v1.0.3 github.com/envoyproxy/protoc-gen-validate v1.0.2 github.com/fatih/color v1.15.0 + github.com/go-jose/go-jose/v3 v3.0.0 github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-webauthn/webauthn v0.8.6 github.com/golang/mock v1.6.0 @@ -48,11 +49,12 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/gamut v0.3.1 github.com/muhlemmer/gu v0.3.1 + github.com/muhlemmer/httpforwarded v0.1.0 github.com/nicksnyder/go-i18n/v2 v2.2.1 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 github.com/rakyll/statik v0.1.7 - github.com/rs/cors v1.10.0 + github.com/rs/cors v1.10.1 github.com/sony/sonyflake v1.2.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 @@ -60,36 +62,35 @@ require ( github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 github.com/zitadel/logging v0.4.0 - github.com/zitadel/oidc/v2 v2.11.0 + github.com/zitadel/oidc/v3 v3.0.2 github.com/zitadel/passwap v0.4.0 github.com/zitadel/saml v0.1.2 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 - go.opentelemetry.io/otel v1.17.0 + go.opentelemetry.io/otel v1.19.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 go.opentelemetry.io/otel/exporters/prometheus v0.40.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 + go.opentelemetry.io/otel/metric v1.19.0 go.opentelemetry.io/otel/sdk v1.17.0 go.opentelemetry.io/otel/sdk/metric v0.40.0 - go.opentelemetry.io/otel/trace v1.17.0 - golang.org/x/crypto v0.13.0 - golang.org/x/net v0.15.0 - golang.org/x/oauth2 v0.12.0 + go.opentelemetry.io/otel/trace v1.19.0 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 + golang.org/x/oauth2 v0.13.0 golang.org/x/sync v0.3.0 golang.org/x/text v0.13.0 google.golang.org/api v0.138.0 google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 - gopkg.in/square/go-jose.v2 v2.6.0 sigs.k8s.io/yaml v1.3.0 ) require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.43.1 // indirect github.com/crewjam/httperr v0.2.0 // indirect - github.com/dmarkham/enumer v1.5.8 // indirect + github.com/go-chi/chi/v5 v5.0.10 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect @@ -105,14 +106,11 @@ require ( github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/muhlemmer/httpforwarded v0.1.0 // indirect - github.com/pascaldekloe/name v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/smartystreets/assertions v1.0.0 // indirect github.com/zenazn/goji v1.0.1 // indirect + github.com/zitadel/schema v1.3.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect ) @@ -154,7 +152,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.3.1 github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect @@ -203,7 +201,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/sys v0.12.0 + golang.org/x/sys v0.13.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 9e7ae585c..018d31f9e 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,6 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dmarkham/enumer v1.5.8 h1:fIF11F9l5jyD++YYvxcSH5WgHfeaSGPaN/T4kOQ4qEM= -github.com/dmarkham/enumer v1.5.8/go.mod h1:d10o8R3t/gROm2p3BXqTkMt2+HMuxEmWCXzorAruYak= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog= @@ -267,6 +265,8 @@ github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= +github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= @@ -275,6 +275,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -720,8 +722,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U= -github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= @@ -782,8 +782,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= -github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -883,12 +883,14 @@ github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zitadel/logging v0.4.0 h1:lRAIFgaRoJpLNbsL7jtIYHcMDoEJP9QZB4GqMfl4xaA= github.com/zitadel/logging v0.4.0/go.mod h1:6uALRJawpkkuUPCkgzfgcPR3c2N908wqnOnIrRelUFc= -github.com/zitadel/oidc/v2 v2.11.0 h1:Am4/yQr4iiM5bznRgF3FOp+wLdKx2gzSU73uyI9vvBE= -github.com/zitadel/oidc/v2 v2.11.0/go.mod h1:enFSVBQI6aE0TEB1ntjXs9r6O6DEosxX4uhEBLBVD8o= +github.com/zitadel/oidc/v3 v3.0.2 h1:fw0EAjx8lIlDMJ54hDz2fWIhpW/Y13tW5gd1qWGqbr4= +github.com/zitadel/oidc/v3 v3.0.2/go.mod h1:ne9V9FHug4iUZDV42JirWVLHcbmwaxY8LnkcfekHgRg= github.com/zitadel/passwap v0.4.0 h1:cMaISx+Ve7ilgG7Q8xOli4Z6IWr8Gndss+jeBk5A3O0= github.com/zitadel/passwap v0.4.0/go.mod h1:yHaDM4A68yRkdic5BZ4iUNoc19hT+kYt8n1/Nz+I87g= github.com/zitadel/saml v0.1.2 h1:RICwNTuP2upX4A1sZ8iq1rv4/x3DhZHzFx1e5bTKoTo= github.com/zitadel/saml v0.1.2/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM= +github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= +github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -905,8 +907,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0 h1:HKORGpiOY0R0nAPtKx/ub8/7XoHhRooP8yNRkuPfelI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.43.0/go.mod h1:e+y1M74SYXo/FcIx3UATwth2+5dDkM8dBi7eXg1tbw8= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 h1:U5GYackKpVKlPrd/5gKMlrTlP2dCESAAFU682VCpieY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0/go.mod h1:aFsJfCEnLzEu9vRRAcUiB/cpRTbVsNdF3OHSPpdjxZQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.17.0 h1:iGeIsSYwpYSvh5UGzWrJfTDJvPjrXtxl3GUppj6IXQU= @@ -915,14 +917,14 @@ go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ER go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= @@ -952,6 +954,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -965,8 +968,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1007,8 +1010,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1059,8 +1060,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1070,8 +1071,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1154,8 +1155,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1242,8 +1243,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1384,8 +1383,6 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 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= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/internal/actions/uuid_module.go b/internal/actions/uuid_module.go new file mode 100644 index 000000000..15d299212 --- /dev/null +++ b/internal/actions/uuid_module.go @@ -0,0 +1,83 @@ +package actions + +import ( + "context" + + "github.com/dop251/goja" + "github.com/google/uuid" + "github.com/zitadel/logging" +) + +func WithUUID(ctx context.Context) Option { + return func(c *runConfig) { + c.modules["zitadel/uuid"] = func(runtime *goja.Runtime, module *goja.Object) { + requireUUID(ctx, runtime, module) + } + } +} + +func requireUUID(_ context.Context, runtime *goja.Runtime, module *goja.Object) { + o := module.Get("exports").(*goja.Object) + logging.OnError(o.Set("v1", inRuntime(uuid.NewUUID, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v3", inRuntimeHash(uuid.NewMD5, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v4", inRuntime(uuid.NewRandom, runtime))).Warn("unable to set module") + logging.OnError(o.Set("v5", inRuntimeHash(uuid.NewSHA1, runtime))).Warn("unable to set module") + logging.OnError(o.Set("namespaceDNS", uuid.NameSpaceDNS)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceURL", uuid.NameSpaceURL)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceOID", uuid.NameSpaceOID)).Warn("unable to set namespace") + logging.OnError(o.Set("namespaceX500", uuid.NameSpaceX500)).Warn("unable to set namespace") +} + +func inRuntime(function func() (uuid.UUID, error), runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value { + return func(call goja.FunctionCall) goja.Value { + if len(call.Arguments) != 0 { + panic("invalid arg count") + } + + uuid, err := function() + if err != nil { + logging.WithError(err) + panic(err) + } + + return runtime.ToValue(uuid.String()) + } +} + +func inRuntimeHash(function func(uuid.UUID, []byte) uuid.UUID, runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value { + return func(call goja.FunctionCall) goja.Value { + if len(call.Arguments) != 2 { + logging.WithFields("count", len(call.Arguments)).Debug("other than 2 args provided") + panic("invalid arg count") + } + + var err error + var namespace uuid.UUID + switch n := call.Arguments[0].Export().(type) { + case string: + namespace, err = uuid.Parse(n) + if err != nil { + logging.WithError(err).Debug("namespace failed parsing as UUID") + panic(err) + } + case uuid.UUID: + namespace = n + default: + logging.WithError(err).Debug("invalid type for namespace") + panic(err) + } + + var data []byte + switch d := call.Arguments[1].Export().(type) { + case string: + data = []byte(d) + case []byte: + data = d + default: + logging.WithError(err).Debug("invalid type for data") + panic(err) + } + + return runtime.ToValue(function(namespace, data).String()) + } +} diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index afb4af4bf..e5b34af9a 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -10,8 +10,8 @@ import ( "sync" "time" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -28,7 +28,7 @@ type TokenVerifier struct { authZRepo authZRepo clients sync.Map authMethods MethodMapping - systemJWTProfile op.JWTProfileVerifier + systemJWTProfile *op.JWTProfileVerifier } type MembershipsResolver interface { diff --git a/internal/api/grpc/management/information.go b/internal/api/grpc/management/information.go index 29ce6ac01..d18e115bf 100644 --- a/internal/api/grpc/management/information.go +++ b/internal/api/grpc/management/information.go @@ -3,7 +3,7 @@ package management import ( "context" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index d7a9c56dc..c61832169 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -4,7 +4,7 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "google.golang.org/protobuf/types/known/durationpb" @@ -647,6 +647,26 @@ func (s *Server) RemoveHumanAuthFactorU2F(ctx context.Context, req *mgmt_pb.Remo }, nil } +func (s *Server) RemoveHumanAuthFactorOTPSMS(ctx context.Context, req *mgmt_pb.RemoveHumanAuthFactorOTPSMSRequest) (*mgmt_pb.RemoveHumanAuthFactorOTPSMSResponse, error) { + objectDetails, err := s.command.RemoveHumanOTPSMS(ctx, req.UserId, authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + return &mgmt_pb.RemoveHumanAuthFactorOTPSMSResponse{ + Details: obj_grpc.DomainToChangeDetailsPb(objectDetails), + }, nil +} + +func (s *Server) RemoveHumanAuthFactorOTPEmail(ctx context.Context, req *mgmt_pb.RemoveHumanAuthFactorOTPEmailRequest) (*mgmt_pb.RemoveHumanAuthFactorOTPEmailResponse, error) { + objectDetails, err := s.command.RemoveHumanOTPEmail(ctx, req.UserId, authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + return &mgmt_pb.RemoveHumanAuthFactorOTPEmailResponse{ + Details: obj_grpc.DomainToChangeDetailsPb(objectDetails), + }, nil +} + func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHumanPasswordlessRequest) (*mgmt_pb.ListHumanPasswordlessResponse, error) { query := new(query.UserAuthMethodSearchQueries) err := query.AppendUserIDQuery(req.UserId) diff --git a/internal/api/grpc/oidc/v2/oidc.go b/internal/api/grpc/oidc/v2/oidc.go index d35a5dcff..465303b98 100644 --- a/internal/api/grpc/oidc/v2/oidc.go +++ b/internal/api/grpc/oidc/v2/oidc.go @@ -4,7 +4,7 @@ import ( "context" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" diff --git a/internal/api/grpc/oidc/v2/oidc_integration_test.go b/internal/api/grpc/oidc/v2/oidc_integration_test.go index b64b71325..db11f9022 100644 --- a/internal/api/grpc/oidc/v2/oidc_integration_test.go +++ b/internal/api/grpc/oidc/v2/oidc_integration_test.go @@ -54,7 +54,7 @@ func TestServer_GetAuthRequest(t *testing.T) { require.NoError(t, err) client, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId()) require.NoError(t, err) - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) now := time.Now() @@ -134,7 +134,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "session not found", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -151,7 +151,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "session token invalid", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -168,7 +168,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "fail callback", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -192,7 +192,7 @@ func TestServer_CreateCallback(t *testing.T) { name: "code callback", req: &oidc_pb.CreateCallbackRequest{ AuthRequestId: func() string { - authRequestID, err := Tester.CreateOIDCAuthRequest(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) + authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURI) require.NoError(t, err) return authRequestID }(), @@ -217,7 +217,7 @@ func TestServer_CreateCallback(t *testing.T) { AuthRequestId: func() string { client, err := Tester.CreateOIDCImplicitFlowClient(CTX, redirectURIImplicit) require.NoError(t, err) - authRequestID, err := Tester.CreateOIDCAuthRequestImplicit(client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURIImplicit) + authRequestID, err := Tester.CreateOIDCAuthRequestImplicit(CTX, client.GetClientId(), Tester.Users[integration.FirstInstanceUsersKey][integration.OrgOwner].ID, redirectURIImplicit) require.NoError(t, err) return authRequestID }(), diff --git a/internal/api/grpc/oidc/v2/server.go b/internal/api/grpc/oidc/v2/server.go index a7587dfdc..823594fbd 100644 --- a/internal/api/grpc/oidc/v2/server.go +++ b/internal/api/grpc/oidc/v2/server.go @@ -1,7 +1,7 @@ package oidc import ( - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "google.golang.org/grpc" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/grpc/server/middleware/auth_interceptor.go b/internal/api/grpc/server/middleware/auth_interceptor.go index 96426e857..d2a81203e 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor.go +++ b/internal/api/grpc/server/middleware/auth_interceptor.go @@ -33,12 +33,7 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, return nil, status.Error(codes.Unauthenticated, "auth header missing") } - var orgDomain string - orgID := grpc_util.GetHeader(authCtx, http.ZitadelOrgID) - if o, ok := req.(OrganisationFromRequest); ok { - orgID = o.OrganisationFromRequest().ID - orgDomain = o.OrganisationFromRequest().Domain - } + orgID, orgDomain := orgIDAndDomainFromRequest(authCtx, req) ctxSetter, err := authz.CheckUserAuthorization(authCtx, req, authToken, orgID, orgDomain, verifier, authConfig, authOpt, info.FullMethod) if err != nil { return nil, err @@ -47,11 +42,38 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, return handler(ctxSetter(ctx), req) } -type OrganisationFromRequest interface { - OrganisationFromRequest() *Organisation +func orgIDAndDomainFromRequest(ctx context.Context, req interface{}) (id, domain string) { + orgID := grpc_util.GetHeader(ctx, http.ZitadelOrgID) + o, ok := req.(OrganizationFromRequest) + if !ok { + return orgID, "" + } + id = o.OrganizationFromRequest().ID + domain = o.OrganizationFromRequest().Domain + if id != "" || domain != "" { + return id, domain + } + // check if the deprecated organisation is used. + // to be removed before going GA (https://github.com/zitadel/zitadel/issues/6718) + id = o.OrganisationFromRequest().ID + domain = o.OrganisationFromRequest().Domain + if id != "" || domain != "" { + return id, domain + } + return orgID, domain } -type Organisation struct { +// Deprecated: will be removed in favor of OrganizationFromRequest (https://github.com/zitadel/zitadel/issues/6718) +type OrganisationFromRequest interface { + OrganisationFromRequest() *Organization +} + +type Organization struct { ID string Domain string } + +type OrganizationFromRequest interface { + OrganizationFromRequest() *Organization + OrganisationFromRequest +} diff --git a/internal/api/grpc/session/v2/session.go b/internal/api/grpc/session/v2/session.go index fe8e0d874..f98983936 100644 --- a/internal/api/grpc/session/v2/session.go +++ b/internal/api/grpc/session/v2/session.go @@ -2,10 +2,13 @@ package session import ( "context" + "net" + "net/http" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/muhlemmer/gu" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/object/v2" "github.com/zitadel/zitadel/internal/command" @@ -41,7 +44,7 @@ func (s *Server) ListSessions(ctx context.Context, req *session.ListSessionsRequ } func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRequest) (*session.CreateSessionResponse, error) { - checks, metadata, err := s.createSessionRequestToCommand(ctx, req) + checks, metadata, userAgent, err := s.createSessionRequestToCommand(ctx, req) if err != nil { return nil, err } @@ -50,7 +53,7 @@ func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRe return nil, err } - set, err := s.command.CreateSession(ctx, cmds, metadata) + set, err := s.command.CreateSession(ctx, cmds, metadata, userAgent) if err != nil { return nil, err } @@ -113,9 +116,34 @@ func sessionToPb(s *query.Session) *session.Session { Sequence: s.Sequence, Factors: factorsToPb(s), Metadata: s.Metadata, + UserAgent: userAgentToPb(s.UserAgent), } } +func userAgentToPb(ua domain.UserAgent) *session.UserAgent { + if ua.IsEmpty() { + return nil + } + + out := &session.UserAgent{ + FingerprintId: ua.FingerprintID, + Description: ua.Description, + } + if ua.IP != nil { + out.Ip = gu.Ptr(ua.IP.String()) + } + if ua.Header == nil { + return out + } + out.Header = make(map[string]*session.UserAgent_HeaderValues, len(ua.Header)) + for k, v := range ua.Header { + out.Header[k] = &session.UserAgent_HeaderValues{ + Values: v, + } + } + return out +} + func factorsToPb(s *query.Session) *session.Factors { user := userFactorToPb(s.UserFactor) if user == nil { @@ -188,6 +216,7 @@ func userFactorToPb(factor query.SessionUserFactor) *session.UserFactor { LoginName: factor.LoginName, DisplayName: factor.DisplayName, OrganisationId: factor.ResourceOwner, + OrganizationId: factor.ResourceOwner, } } @@ -236,12 +265,30 @@ func idsQueryToQuery(q *session.IDsQuery) (query.SearchQuery, error) { return query.NewSessionIDsSearchQuery(q.Ids) } -func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, error) { +func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, *domain.UserAgent, error) { checks, err := s.checksToCommand(ctx, req.Checks) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return checks, req.GetMetadata(), nil + return checks, req.GetMetadata(), userAgentToCommand(req.GetUserAgent()), nil +} + +func userAgentToCommand(userAgent *session.UserAgent) *domain.UserAgent { + if userAgent == nil { + return nil + } + out := &domain.UserAgent{ + FingerprintID: userAgent.FingerprintId, + IP: net.ParseIP(userAgent.GetIp()), + Description: userAgent.Description, + } + if len(userAgent.Header) > 0 { + out.Header = make(http.Header, len(userAgent.Header)) + for k, values := range userAgent.Header { + out.Header[k] = values.GetValues() + } + } + return out } func (s *Server) setSessionRequestToCommand(ctx context.Context, req *session.SetSessionRequest) ([]command.SessionCommand, error) { diff --git a/internal/api/grpc/session/v2/session_integration_test.go b/internal/api/grpc/session/v2/session_integration_test.go index 1dcad7bc5..9aba59ee3 100644 --- a/internal/api/grpc/session/v2/session_integration_test.go +++ b/internal/api/grpc/session/v2/session_integration_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" "github.com/zitadel/zitadel/internal/integration" object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta" @@ -53,7 +54,7 @@ func TestMain(m *testing.M) { }()) } -func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, factors ...wantFactor) *session.Session { +func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, userAgent *session.UserAgent, factors ...wantFactor) *session.Session { t.Helper() require.NotEmpty(t, id) require.NotEmpty(t, token) @@ -70,6 +71,11 @@ func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, windo assert.WithinRange(t, s.GetChangeDate().AsTime(), time.Now().Add(-window), time.Now().Add(window)) assert.Equal(t, sequence, s.GetSequence()) assert.Equal(t, metadata, s.GetMetadata()) + + if !proto.Equal(userAgent, s.GetUserAgent()) { + t.Errorf("user agent =\n%v\nwant\n%v", s.GetUserAgent(), userAgent) + } + verifyFactors(t, s.GetFactors(), window, factors) return s } @@ -131,11 +137,12 @@ func verifyFactors(t testing.TB, factors *session.Factors, window time.Duration, func TestServer_CreateSession(t *testing.T) { tests := []struct { - name string - req *session.CreateSessionRequest - want *session.CreateSessionResponse - wantErr bool - wantFactors []wantFactor + name string + req *session.CreateSessionRequest + want *session.CreateSessionResponse + wantErr bool + wantFactors []wantFactor + wantUserAgent *session.UserAgent }{ { name: "empty session", @@ -148,6 +155,33 @@ func TestServer_CreateSession(t *testing.T) { }, }, }, + { + name: "user agent", + req: &session.CreateSessionRequest{ + Metadata: map[string][]byte{"foo": []byte("bar")}, + UserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("Description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, + }, + want: &session.CreateSessionResponse{ + Details: &object.Details{ + ResourceOwner: Tester.Organisation.ID, + }, + }, + wantUserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("Description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, + }, { name: "with user", req: &session.CreateSessionRequest{ @@ -219,7 +253,7 @@ func TestServer_CreateSession(t *testing.T) { require.NoError(t, err) integration.AssertDetails(t, tt.want, got) - verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantFactors...) + verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantUserAgent, tt.wantFactors...) }) } } @@ -242,7 +276,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) assertionData, err := Tester.WebAuthN.CreateAssertionResponse(createResp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true) require.NoError(t, err) @@ -258,7 +292,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified) } func TestServer_CreateSession_successfulIntent(t *testing.T) { @@ -274,7 +308,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id") updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ @@ -288,7 +322,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantIntentFactor) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor) } func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { @@ -304,7 +338,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) idpUserID := "id" intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", idpUserID) @@ -331,7 +365,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantIntentFactor) + verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor) } func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { @@ -347,7 +381,7 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil) intentID := Tester.CreateIntent(t, idpID) _, err = Client.SetSession(CTX, &session.SetSessionRequest{ @@ -399,7 +433,7 @@ func TestServer_SetSession_flow(t *testing.T) { createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{}) require.NoError(t, err) sessionToken := createResp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil, nil) t.Run("check user", func(t *testing.T) { resp, err := Client.SetSession(CTX, &session.SetSessionRequest{ @@ -415,7 +449,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor) }) t.Run("check webauthn, user verified (passkey)", func(t *testing.T) { @@ -430,7 +464,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true) @@ -447,7 +481,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified) }) userAuthCtx := Tester.WithAuthorizationToken(CTX, sessionToken) @@ -474,7 +508,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), false) @@ -491,7 +525,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor) }) } }) @@ -510,7 +544,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor) }) t.Run("check OTP SMS", func(t *testing.T) { @@ -522,7 +556,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() otp := resp.GetChallenges().GetOtpSms() @@ -539,7 +573,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor) }) t.Run("check OTP Email", func(t *testing.T) { @@ -553,7 +587,7 @@ func TestServer_SetSession_flow(t *testing.T) { }, }) require.NoError(t, err) - verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil) + verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil) sessionToken = resp.GetSessionToken() otp := resp.GetChallenges().GetOtpEmail() @@ -570,7 +604,7 @@ func TestServer_SetSession_flow(t *testing.T) { }) require.NoError(t, err) sessionToken = resp.GetSessionToken() - verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor) + verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor) }) } diff --git a/internal/api/grpc/session/v2/session_test.go b/internal/api/grpc/session/v2/session_test.go index 33804caba..8422b675b 100644 --- a/internal/api/grpc/session/v2/session_test.go +++ b/internal/api/grpc/session/v2/session_test.go @@ -2,15 +2,19 @@ package session import ( "context" + "net" + "net/http" "testing" "time" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/query" @@ -23,7 +27,7 @@ func Test_sessionsToPb(t *testing.T) { past := now.Add(-time.Hour) sessions := []*query.Session{ - { // no factor + { // no factor, with user agent ID: "999", CreationDate: now, ChangeDate: now, @@ -32,6 +36,12 @@ func Test_sessionsToPb(t *testing.T) { ResourceOwner: "me", Creator: "he", Metadata: map[string][]byte{"hello": []byte("world")}, + UserAgent: domain.UserAgent{ + FingerprintID: gu.Ptr("fingerprintID"), + Description: gu.Ptr("description"), + IP: net.IPv4(1, 2, 3, 4), + Header: http.Header{"foo": []string{"foo", "bar"}}, + }, }, { // user factor ID: "999", @@ -114,13 +124,21 @@ func Test_sessionsToPb(t *testing.T) { } want := []*session.Session{ - { // no factor + { // no factor, with user agent Id: "999", CreationDate: timestamppb.New(now), ChangeDate: timestamppb.New(now), Sequence: 123, Factors: nil, Metadata: map[string][]byte{"hello": []byte("world")}, + UserAgent: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerprintID"), + Description: gu.Ptr("description"), + Ip: gu.Ptr("1.2.3.4"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + }, + }, }, { // user factor Id: "999", @@ -134,6 +152,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, }, Metadata: map[string][]byte{"hello": []byte("world")}, @@ -150,6 +169,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, Password: &session.PasswordFactor{ VerifiedAt: timestamppb.New(past), @@ -169,6 +189,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, WebAuthN: &session.WebAuthNFactor{ VerifiedAt: timestamppb.New(past), @@ -189,6 +210,7 @@ func Test_sessionsToPb(t *testing.T) { LoginName: "donald", DisplayName: "donald duck", OrganisationId: "org1", + OrganizationId: "org1", }, Totp: &session.TOTPFactor{ VerifiedAt: timestamppb.New(past), @@ -208,6 +230,71 @@ func Test_sessionsToPb(t *testing.T) { } } +func Test_userAgentToPb(t *testing.T) { + type args struct { + ua domain.UserAgent + } + tests := []struct { + name string + args args + want *session.UserAgent + }{ + { + name: "empty", + args: args{domain.UserAgent{}}, + }, + { + name: "fingerprint id and description", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + }, + }, + { + name: "with ip", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + IP: net.IPv4(1, 2, 3, 4), + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Ip: gu.Ptr("1.2.3.4"), + }, + }, + { + name: "with header", + args: args{domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Header: http.Header{ + "foo": []string{"foo", "bar"}, + "hello": []string{"world"}, + }, + }}, + want: &session.UserAgent{ + FingerprintId: gu.Ptr("fingerPrintID"), + Description: gu.Ptr("description"), + Header: map[string]*session.UserAgent_HeaderValues{ + "foo": {Values: []string{"foo", "bar"}}, + "hello": {Values: []string{"world"}}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := userAgentToPb(tt.args.ua) + assert.Equal(t, tt.want, got) + }) + } +} + func mustNewTextQuery(t testing.TB, column query.Column, value string, compare query.TextComparison) query.SearchQuery { q, err := query.NewTextQuery(column, value, compare) require.NoError(t, err) @@ -510,3 +597,73 @@ func Test_userVerificationRequirementToDomain(t *testing.T) { }) } } + +func Test_userAgentToCommand(t *testing.T) { + type args struct { + userAgent *session.UserAgent + } + tests := []struct { + name string + args args + want *domain.UserAgent + }{ + { + name: "nil", + args: args{nil}, + want: nil, + }, + { + name: "all fields", + args: args{&session.UserAgent{ + FingerprintId: gu.Ptr("fp1"), + Ip: gu.Ptr("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: map[string]*session.UserAgent_HeaderValues{ + "hello": { + Values: []string{"foo", "bar"}, + }, + }, + }}, + want: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{ + "hello": []string{"foo", "bar"}, + }, + }, + }, + { + name: "invalid ip", + args: args{&session.UserAgent{ + FingerprintId: gu.Ptr("fp1"), + Ip: gu.Ptr("oops"), + Description: gu.Ptr("firefox"), + Header: map[string]*session.UserAgent_HeaderValues{ + "hello": { + Values: []string{"foo", "bar"}, + }, + }, + }}, + want: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: nil, + Description: gu.Ptr("firefox"), + Header: http.Header{ + "hello": []string{"foo", "bar"}, + }, + }, + }, + { + name: "nil fields", + args: args{&session.UserAgent{}}, + want: &domain.UserAgent{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := userAgentToCommand(tt.args.userAgent) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/api/grpc/system/instance_converter.go b/internal/api/grpc/system/instance_converter.go index 4eea0a18f..551079aec 100644 --- a/internal/api/grpc/system/instance_converter.go +++ b/internal/api/grpc/system/instance_converter.go @@ -3,7 +3,7 @@ package system import ( "strings" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/grpc/authn" diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 4ba077d04..56c0902b1 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -7,8 +7,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_utils "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/api/oidc/auth_request_converter.go b/internal/api/oidc/auth_request_converter.go index 5999f21e5..8fbd18530 100644 --- a/internal/api/oidc/auth_request_converter.go +++ b/internal/api/oidc/auth_request_converter.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/oidc/auth_request_converter_v2.go b/internal/api/oidc/auth_request_converter_v2.go index 9f4c0d0a1..3a35b0157 100644 --- a/internal/api/oidc/auth_request_converter_v2.go +++ b/internal/api/oidc/auth_request_converter_v2.go @@ -3,7 +3,7 @@ package oidc import ( "time" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/command" ) diff --git a/internal/api/oidc/auth_request_integration_test.go b/internal/api/oidc/auth_request_integration_test.go index 532814bee..5b013fe86 100644 --- a/internal/api/oidc/auth_request_integration_test.go +++ b/internal/api/oidc/auth_request_integration_test.go @@ -11,8 +11,8 @@ import ( "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" http_utils "github.com/zitadel/zitadel/internal/api/http" oidc_api "github.com/zitadel/zitadel/internal/api/oidc" @@ -103,7 +103,7 @@ func TestOPStorage_CreateAccessToken_implicit(t *testing.T) { assert.Equal(t, "state", values.Get("state")) // check id_token / claims - provider, err := Tester.CreateRelyingParty(clientID, redirectURIImplicit) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURIImplicit) require.NoError(t, err) claims, err := rp.VerifyTokens[*oidc.IDTokenClaims](context.Background(), accessToken, idToken, provider.IDTokenVerifier()) require.NoError(t, err) @@ -147,7 +147,7 @@ func TestOPStorage_CreateAccessAndRefreshTokens_code(t *testing.T) { func TestOPStorage_CreateAccessAndRefreshTokens_refresh(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -177,13 +177,13 @@ func TestOPStorage_CreateAccessAndRefreshTokens_refresh(t *testing.T) { assertIDTokenClaims(t, newTokens.IDTokenClaims, armPasskey, startTime, changeTime) // refresh with an old refresh_token must fail - _, err = rp.RefreshAccessToken(provider, tokens.RefreshToken, "", "") + _, err = rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, tokens.RefreshToken, "", "") require.Error(t, err) } func TestOPStorage_RevokeToken_access_token(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -206,11 +206,11 @@ func TestOPStorage_RevokeToken_access_token(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke access token - err = rp.RevokeToken(provider, tokens.AccessToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "access_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh grant must still work @@ -218,15 +218,15 @@ func TestOPStorage_RevokeToken_access_token(t *testing.T) { require.NoError(t, err) // revocation with the same access token must not fail (with or without hint) - err = rp.RevokeToken(provider, tokens.AccessToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "access_token") require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.AccessToken, "") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "") require.NoError(t, err) } func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -249,11 +249,11 @@ func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke access token - err = rp.RevokeToken(provider, tokens.AccessToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "refresh_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh grant must still work @@ -263,7 +263,7 @@ func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -286,11 +286,11 @@ func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke refresh token -> invalidates also access token - err = rp.RevokeToken(provider, tokens.RefreshToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "refresh_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh must fail @@ -298,15 +298,15 @@ func TestOPStorage_RevokeToken_refresh_token(t *testing.T) { require.Error(t, err) // revocation with the same refresh token must not fail (with or without hint) - err = rp.RevokeToken(provider, tokens.RefreshToken, "refresh_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "refresh_token") require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.RefreshToken, "") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "") require.NoError(t, err) } func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -329,11 +329,11 @@ func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing. assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // revoke refresh token even with a wrong hint - err = rp.RevokeToken(provider, tokens.RefreshToken, "access_token") + err = rp.RevokeToken(CTX, provider, tokens.RefreshToken, "access_token") require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) // refresh must fail @@ -365,15 +365,15 @@ func TestOPStorage_RevokeToken_invalid_client(t *testing.T) { // simulate second client (not part of the audience) trying to revoke the token otherClientID := createClient(t) - provider, err := Tester.CreateRelyingParty(otherClientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, otherClientID, redirectURI) require.NoError(t, err) - err = rp.RevokeToken(provider, tokens.AccessToken, "") + err = rp.RevokeToken(CTX, provider, tokens.AccessToken, "") require.Error(t, err) } func TestOPStorage_TerminateSession(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -396,21 +396,21 @@ func TestOPStorage_TerminateSession(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // userinfo must not fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) } func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -433,28 +433,28 @@ func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // userinfo must not fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) refreshedTokens, err := refreshTokens(t, clientID, tokens.RefreshToken) require.NoError(t, err) // userinfo must not fail - _, err = rp.Userinfo(refreshedTokens.AccessToken, refreshedTokens.TokenType, refreshedTokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, refreshedTokens.AccessToken, refreshedTokens.TokenType, refreshedTokens.IDTokenClaims.Subject, provider) require.NoError(t, err) } func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -476,12 +476,12 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { assertTokens(t, tokens, false) assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) - postLogoutRedirect, err := rp.EndSession(provider, "", logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, "", logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, http_utils.BuildOrigin(Tester.Host(), Tester.Config.ExternalSecure)+Tester.Config.OIDC.DefaultLogoutURLV2+logoutRedirectURI+"?state=state", postLogoutRedirect.String()) // userinfo must not fail until login UI terminated session - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) // simulate termination by login UI @@ -492,12 +492,12 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) { require.NoError(t, err) // userinfo must fail - _, err = rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + _, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.Error(t, err) } func exchangeTokens(t testing.TB, clientID, code string) (*oidc.Tokens[*oidc.IDTokenClaims], error) { - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) codeVerifier := "codeVerifier" @@ -505,23 +505,10 @@ func exchangeTokens(t testing.TB, clientID, code string) (*oidc.Tokens[*oidc.IDT } func refreshTokens(t testing.TB, clientID, refreshToken string) (*oidc.Tokens[*oidc.IDTokenClaims], error) { - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) - tokens, err := rp.RefreshAccessToken(provider, refreshToken, "", "") - if err != nil { - return nil, err - } - idToken, _ := tokens.Extra("id_token").(string) - claims, err := rp.VerifyTokens[*oidc.IDTokenClaims](context.Background(), tokens.AccessToken, idToken, provider.IDTokenVerifier()) - if err != nil { - return nil, err - } - return &oidc.Tokens[*oidc.IDTokenClaims]{ - Token: tokens, - IDToken: idToken, - IDTokenClaims: claims, - }, nil + return rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, refreshToken, "", "") } func assertCodeResponse(t *testing.T, callback string) string { diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 6a8dc6be6..7c5628817 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -9,10 +9,10 @@ import ( "time" "github.com/dop251/goja" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/actions" "github.com/zitadel/zitadel/internal/actions/object" @@ -564,7 +564,7 @@ func (o *OPStorage) userinfoFlows(ctx context.Context, user *query.User, userGra apiFields, action.Script, action.Name, - append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -745,7 +745,7 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG apiFields, action.Script, action.Name, - append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { diff --git a/internal/api/oidc/client_converter.go b/internal/api/oidc/client_converter.go index 5f3f25f75..ec208db27 100644 --- a/internal/api/oidc/client_converter.go +++ b/internal/api/oidc/client_converter.go @@ -4,8 +4,8 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/api/oidc/client_credentials.go b/internal/api/oidc/client_credentials.go index fda1f6c94..3c2f272ea 100644 --- a/internal/api/oidc/client_credentials.go +++ b/internal/api/oidc/client_credentials.go @@ -3,8 +3,8 @@ package oidc import ( "time" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) type clientCredentialsRequest struct { diff --git a/internal/api/oidc/client_integration_test.go b/internal/api/oidc/client_integration_test.go index 11121a8fa..8a7f58a44 100644 --- a/internal/api/oidc/client_integration_test.go +++ b/internal/api/oidc/client_integration_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/client/rs" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/pkg/grpc/authn" "github.com/zitadel/zitadel/pkg/grpc/management" @@ -41,9 +41,9 @@ func TestOPStorage_SetUserinfoFromToken(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // test actual userinfo - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) - userinfo, err := rp.Userinfo(tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) + userinfo, err := rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider) require.NoError(t, err) assertUserinfo(t, userinfo) } @@ -62,7 +62,7 @@ func TestOPStorage_SetIntrospectionFromToken(t *testing.T) { ExpirationDate: nil, }) require.NoError(t, err) - resourceServer, err := Tester.CreateResourceServer(keyResp.GetKeyDetails()) + resourceServer, err := Tester.CreateResourceServer(CTX, keyResp.GetKeyDetails()) require.NoError(t, err) scope := []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess} @@ -87,7 +87,7 @@ func TestOPStorage_SetIntrospectionFromToken(t *testing.T) { assertIDTokenClaims(t, tokens.IDTokenClaims, armPasskey, startTime, changeTime) // test actual introspection - introspection, err := rs.Introspect(context.Background(), resourceServer, tokens.AccessToken) + introspection, err := rs.Introspect[*oidc.IntrospectionResponse](context.Background(), resourceServer, tokens.AccessToken) require.NoError(t, err) assertIntrospection(t, introspection, Tester.OIDCIssuer(), app.GetClientId(), diff --git a/internal/api/oidc/device_auth.go b/internal/api/oidc/device_auth.go index 7eee06096..80298e8af 100644 --- a/internal/api/oidc/device_auth.go +++ b/internal/api/oidc/device_auth.go @@ -5,8 +5,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/ui/login" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/api/oidc/jwt-profile.go b/internal/api/oidc/jwt-profile.go index 47805783c..e2592a573 100644 --- a/internal/api/oidc/jwt-profile.go +++ b/internal/api/oidc/jwt-profile.go @@ -3,8 +3,8 @@ package oidc import ( "context" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" diff --git a/internal/api/oidc/key.go b/internal/api/oidc/key.go index 82884101d..3a2a6ae32 100644 --- a/internal/api/oidc/key.go +++ b/internal/api/oidc/key.go @@ -5,9 +5,9 @@ import ( "fmt" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/api/oidc/oidc_integration_test.go b/internal/api/oidc/oidc_integration_test.go index 1a1516040..7738e3d61 100644 --- a/internal/api/oidc/oidc_integration_test.go +++ b/internal/api/oidc/oidc_integration_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "google.golang.org/grpc/metadata" "github.com/zitadel/zitadel/internal/domain" @@ -216,7 +216,7 @@ func Test_ZITADEL_API_inactive_access_token(t *testing.T) { func Test_ZITADEL_API_terminated_session(t *testing.T) { clientID := createClient(t) - provider, err := Tester.CreateRelyingParty(clientID, redirectURI) + provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI) require.NoError(t, err) authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope) sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId()) @@ -245,7 +245,7 @@ func Test_ZITADEL_API_terminated_session(t *testing.T) { require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId()) // refresh token - postLogoutRedirect, err := rp.EndSession(provider, tokens.IDToken, logoutRedirectURI, "state") + postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state") require.NoError(t, err) assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String()) @@ -271,13 +271,13 @@ func createImplicitClient(t testing.TB) string { } func createAuthRequest(t testing.TB, clientID, redirectURI string, scope ...string) string { - redURL, err := Tester.CreateOIDCAuthRequest(clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) + redURL, err := Tester.CreateOIDCAuthRequest(CTX, clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) require.NoError(t, err) return redURL } func createAuthRequestImplicit(t testing.TB, clientID, redirectURI string, scope ...string) string { - redURL, err := Tester.CreateOIDCAuthRequestImplicit(clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) + redURL, err := Tester.CreateOIDCAuthRequestImplicit(CTX, clientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, scope...) require.NoError(t, err) return redURL } diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index da96302e6..c165b117b 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -7,8 +7,8 @@ import ( "time" "github.com/rakyll/statik/fs" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/assets" diff --git a/internal/api/saml/certificate.go b/internal/api/saml/certificate.go index d2bf3bf3e..6dd02e1bc 100644 --- a/internal/api/saml/certificate.go +++ b/internal/api/saml/certificate.go @@ -5,9 +5,9 @@ import ( "fmt" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" "github.com/zitadel/saml/pkg/provider/key" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/api/ui/console/console.go b/internal/api/ui/console/console.go index 7e00605b7..6f632136f 100644 --- a/internal/api/ui/console/console.go +++ b/internal/api/ui/console/console.go @@ -15,7 +15,7 @@ import ( "github.com/gorilla/mux" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/cmd/build" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/api/ui/login/custom_action.go b/internal/api/ui/login/custom_action.go index 516f7bc3d..f7287dd1d 100644 --- a/internal/api/ui/login/custom_action.go +++ b/internal/api/ui/login/custom_action.go @@ -7,7 +7,7 @@ import ( "github.com/dop251/goja" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/actions" @@ -133,7 +133,7 @@ func (l *Login) runPostExternalAuthenticationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -206,7 +206,7 @@ func (l *Login) runPostInternalAuthenticationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -307,7 +307,7 @@ func (l *Login) runPreCreationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { @@ -365,7 +365,7 @@ func (l *Login) runPostCreationActions( apiFields, a.Script, a.Name, - append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., + append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))..., ) cancel() if err != nil { diff --git a/internal/api/ui/login/external_provider_handler.go b/internal/api/ui/login/external_provider_handler.go index c9cc94203..d9c8c04fc 100644 --- a/internal/api/ui/login/external_provider_handler.go +++ b/internal/api/ui/login/external_provider_handler.go @@ -7,8 +7,8 @@ import ( "github.com/crewjam/saml/samlsp" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/api/ui/login/jwt_handler.go b/internal/api/ui/login/jwt_handler.go index 51b079584..aa7a4466d 100644 --- a/internal/api/ui/login/jwt_handler.go +++ b/internal/api/ui/login/jwt_handler.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" http_util "github.com/zitadel/zitadel/internal/api/http" diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index d67b2dcc7..42a9ba350 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -7,10 +7,10 @@ import ( "strings" "time" + "github.com/go-jose/go-jose/v3" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" - "github.com/zitadel/oidc/v2/pkg/op" - "gopkg.in/square/go-jose.v2" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_util "github.com/zitadel/zitadel/internal/api/http" @@ -274,7 +274,7 @@ func (repo *TokenVerifierRepo) getTokenIDAndSubject(ctx context.Context, accessT return splitToken[0], splitToken[1], true } -func (repo *TokenVerifierRepo) jwtTokenVerifier(ctx context.Context) op.AccessTokenVerifier { +func (repo *TokenVerifierRepo) jwtTokenVerifier(ctx context.Context) *op.AccessTokenVerifier { keySet := &openIDKeySet{repo.Query} issuer := http_util.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), repo.ExternalSecure) return op.NewAccessTokenVerifier(issuer, keySet) diff --git a/internal/command/auth_request_test.go b/internal/command/auth_request_test.go index 0c1bb1cf3..183e1b57c 100644 --- a/internal/command/auth_request_test.go +++ b/internal/command/auth_request_test.go @@ -2,6 +2,8 @@ package command import ( "context" + "net" + "net/http" "testing" "time" @@ -354,7 +356,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { @@ -397,7 +407,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), ), ), tokenVerifier: func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) { @@ -440,8 +458,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate), - ), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, "userID", testNow), @@ -517,8 +542,15 @@ func TestCommands_LinkSessionToAuthRequest(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate), - ), + session.NewAddedEvent(mockCtx, + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewUserCheckedEvent(mockCtx, &session.NewAggregate("sessionID", "org1").Aggregate, "userID", testNow), diff --git a/internal/command/idp_intent.go b/internal/command/idp_intent.go index 265293a3a..edf28ef46 100644 --- a/internal/command/idp_intent.go +++ b/internal/command/idp_intent.go @@ -9,7 +9,7 @@ import ( "github.com/crewjam/saml" "github.com/crewjam/saml/samlsp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index 4f5b35c33..400b0faf0 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -9,7 +9,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index b4d932fb2..e9949fb40 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -7,7 +7,7 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rp" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 3e45d8266..8989f4c92 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" diff --git a/internal/command/oidc_session_test.go b/internal/command/oidc_session_test.go index ab850aeea..a517814e0 100644 --- a/internal/command/oidc_session_test.go +++ b/internal/command/oidc_session_test.go @@ -2,6 +2,8 @@ package command import ( "context" + "net" + "net/http" "testing" "time" @@ -163,7 +165,15 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), ), eventFromEventPusher( session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, @@ -356,7 +366,15 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) { ), expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), ), eventFromEventPusher( session.NewUserCheckedEvent(context.Background(), &session.NewAggregate("sessionID", "instanceID").Aggregate, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index d6eef4a39..8f966e740 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/command/org_test.go b/internal/command/org_test.go index 6db8654d9..6f00765bc 100644 --- a/internal/command/org_test.go +++ b/internal/command/org_test.go @@ -7,7 +7,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/api/authz" diff --git a/internal/command/session.go b/internal/command/session.go index a58c5d2d3..caf3056f7 100644 --- a/internal/command/session.go +++ b/internal/command/session.go @@ -166,8 +166,8 @@ func (s *SessionCommands) Exec(ctx context.Context) error { return nil } -func (s *SessionCommands) Start(ctx context.Context) { - s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate)) +func (s *SessionCommands) Start(ctx context.Context, userAgent *domain.UserAgent) { + s.eventCommands = append(s.eventCommands, session.NewAddedEvent(ctx, s.sessionWriteModel.aggregate, userAgent)) } func (s *SessionCommands) UserChecked(ctx context.Context, userID string, checkedAt time.Time) error { @@ -280,7 +280,7 @@ func (s *SessionCommands) commands(ctx context.Context) (string, []eventstore.Co return token, s.eventCommands, nil } -func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte) (set *SessionChanged, err error) { +func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, metadata map[string][]byte, userAgent *domain.UserAgent) (set *SessionChanged, err error) { sessionID, err := c.idGenerator.Next() if err != nil { return nil, err @@ -291,7 +291,7 @@ func (c *Commands) CreateSession(ctx context.Context, cmds []SessionCommand, met return nil, err } cmd := c.NewSessionCommands(cmds, sessionWriteModel) - cmd.Start(ctx) + cmd.Start(ctx, userAgent) return c.updateSession(ctx, cmd, metadata) } diff --git a/internal/command/session_test.go b/internal/command/session_test.go index e478792e1..7e443a4c5 100644 --- a/internal/command/session_test.go +++ b/internal/command/session_test.go @@ -3,10 +3,13 @@ package command import ( "context" "io" + "net" + "net/http" "testing" "time" "github.com/golang/mock/gomock" + "github.com/muhlemmer/gu" "github.com/pquerna/otp/totp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -145,9 +148,10 @@ func TestCommands_CreateSession(t *testing.T) { tokenCreator func(sessionID string) (string, string, error) } type args struct { - ctx context.Context - checks []SessionCommand - metadata map[string][]byte + ctx context.Context + checks []SessionCommand + metadata map[string][]byte + userAgent *domain.UserAgent } type res struct { want *SessionChanged @@ -200,11 +204,25 @@ func TestCommands_CreateSession(t *testing.T) { }, args{ ctx: authz.NewMockContext("", "org1", ""), + userAgent: &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, }, []expect{ expectFilter(), expectPush( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + ), session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID", ), @@ -227,7 +245,7 @@ func TestCommands_CreateSession(t *testing.T) { idGenerator: tt.fields.idGenerator, sessionTokenCreator: tt.fields.tokenCreator, } - got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata) + got, err := c.CreateSession(tt.args.ctx, tt.args.checks, tt.args.metadata, tt.args.userAgent) require.ErrorIs(t, err, tt.res.err) assert.Equal(t, tt.res.want, got) }) @@ -276,7 +294,15 @@ func TestCommands_UpdateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -301,7 +327,15 @@ func TestCommands_UpdateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -866,7 +900,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -891,7 +933,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID")), @@ -920,7 +970,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID"), @@ -950,7 +1008,15 @@ func TestCommands_TerminateSession(t *testing.T) { eventstore: eventstoreExpect(t, expectFilter( eventFromEventPusher( - session.NewAddedEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate)), + session.NewAddedEvent(context.Background(), + &session.NewAggregate("sessionID", "org1").Aggregate, + &domain.UserAgent{ + FingerprintID: gu.Ptr("fp1"), + IP: net.ParseIP("1.2.3.4"), + Description: gu.Ptr("firefox"), + Header: http.Header{"foo": []string{"bar"}}, + }, + )), eventFromEventPusher( session.NewTokenSetEvent(context.Background(), &session.NewAggregate("sessionID", "org1").Aggregate, "tokenID"), diff --git a/internal/command/user_human_refresh_token_test.go b/internal/command/user_human_refresh_token_test.go index 6ad7f6865..c623c737b 100644 --- a/internal/command/user_human_refresh_token_test.go +++ b/internal/command/user_human_refresh_token_test.go @@ -8,7 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/crypto/passwap.go b/internal/crypto/passwap.go index a5a293a44..479d5731e 100644 --- a/internal/crypto/passwap.go +++ b/internal/crypto/passwap.go @@ -147,7 +147,7 @@ func (c *HasherConfig) buildHasher() (hasher passwap.Hasher, prefixes []string, func (c *HasherConfig) decodeParams(dst any) error { decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - ErrorUnused: true, + ErrorUnused: false, ErrorUnset: true, Result: dst, }) diff --git a/internal/crypto/passwap_test.go b/internal/crypto/passwap_test.go index b557ca4a5..0538ac631 100644 --- a/internal/crypto/passwap_test.go +++ b/internal/crypto/passwap_test.go @@ -379,7 +379,10 @@ func TestHasherConfig_decodeParams(t *testing.T) { "b": 2, "c": 3, }, - wantErr: true, + want: dst{ + A: 1, + B: 2, + }, }, { name: "unset", diff --git a/internal/domain/user_agent.go b/internal/domain/user_agent.go new file mode 100644 index 000000000..ca72c6bec --- /dev/null +++ b/internal/domain/user_agent.go @@ -0,0 +1,17 @@ +package domain + +import ( + "net" + httplib "net/http" +) + +type UserAgent struct { + FingerprintID *string `json:"fingerprint_id,omitempty"` + IP net.IP `json:"ip,omitempty"` + Description *string `json:"description,omitempty"` + Header httplib.Header `json:"header,omitempty"` +} + +func (ua UserAgent) IsEmpty() bool { + return ua.FingerprintID == nil && len(ua.IP) == 0 && ua.Description == nil && ua.Header == nil +} diff --git a/internal/idp/providers/apple/apple.go b/internal/idp/providers/apple/apple.go index 9463cb61b..9664e006c 100644 --- a/internal/idp/providers/apple/apple.go +++ b/internal/idp/providers/apple/apple.go @@ -5,9 +5,9 @@ import ( "encoding/pem" "time" - "github.com/zitadel/oidc/v2/pkg/crypto" - openid "github.com/zitadel/oidc/v2/pkg/oidc" - "gopkg.in/square/go-jose.v2" + "github.com/go-jose/go-jose/v3" + "github.com/zitadel/oidc/v3/pkg/crypto" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/apple/session.go b/internal/idp/providers/apple/session.go index ae629f957..5e9143f05 100644 --- a/internal/idp/providers/apple/session.go +++ b/internal/idp/providers/apple/session.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/apple/session_test.go b/internal/idp/providers/apple/session_test.go index 207f21981..3c1ab5976 100644 --- a/internal/idp/providers/apple/session_test.go +++ b/internal/idp/providers/apple/session_test.go @@ -9,7 +9,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/azuread/azuread.go b/internal/idp/providers/azuread/azuread.go index 244f383e1..46445a397 100644 --- a/internal/idp/providers/azuread/azuread.go +++ b/internal/idp/providers/azuread/azuread.go @@ -3,7 +3,7 @@ package azuread import ( "fmt" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/azuread/azuread_test.go b/internal/idp/providers/azuread/azuread_test.go index 3febb43f9..122a70bb0 100644 --- a/internal/idp/providers/azuread/azuread_test.go +++ b/internal/idp/providers/azuread/azuread_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oauth" diff --git a/internal/idp/providers/azuread/session.go b/internal/idp/providers/azuread/session.go index 5bc7bb84c..698cdad19 100644 --- a/internal/idp/providers/azuread/session.go +++ b/internal/idp/providers/azuread/session.go @@ -3,8 +3,8 @@ package azuread import ( "net/http" - httphelper "github.com/zitadel/oidc/v2/pkg/http" - "github.com/zitadel/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp/providers/oauth" ) diff --git a/internal/idp/providers/azuread/session_test.go b/internal/idp/providers/azuread/session_test.go index 531d909b9..f68c4cc7d 100644 --- a/internal/idp/providers/azuread/session_test.go +++ b/internal/idp/providers/azuread/session_test.go @@ -10,7 +10,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/github/session_test.go b/internal/idp/providers/github/session_test.go index 2ad9f449f..247ef35c6 100644 --- a/internal/idp/providers/github/session_test.go +++ b/internal/idp/providers/github/session_test.go @@ -10,7 +10,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/gitlab/gitlab.go b/internal/idp/providers/gitlab/gitlab.go index 1bb02302f..76e9a74b4 100644 --- a/internal/idp/providers/gitlab/gitlab.go +++ b/internal/idp/providers/gitlab/gitlab.go @@ -1,7 +1,7 @@ package gitlab import ( - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/gitlab/session_test.go b/internal/idp/providers/gitlab/session_test.go index de5904432..0853f3ce3 100644 --- a/internal/idp/providers/gitlab/session_test.go +++ b/internal/idp/providers/gitlab/session_test.go @@ -9,8 +9,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/google/google.go b/internal/idp/providers/google/google.go index 7036d4e10..221f2b61a 100644 --- a/internal/idp/providers/google/google.go +++ b/internal/idp/providers/google/google.go @@ -1,7 +1,7 @@ package google import ( - openid "github.com/zitadel/oidc/v2/pkg/oidc" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" diff --git a/internal/idp/providers/google/session_test.go b/internal/idp/providers/google/session_test.go index d3edde0ea..1915adf1b 100644 --- a/internal/idp/providers/google/session_test.go +++ b/internal/idp/providers/google/session_test.go @@ -9,8 +9,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - openid "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + openid "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/jwt/session.go b/internal/idp/providers/jwt/session.go index be3ffc053..54fcc039e 100644 --- a/internal/idp/providers/jwt/session.go +++ b/internal/idp/providers/jwt/session.go @@ -8,8 +8,8 @@ import ( "time" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/idp/providers/jwt/session_test.go b/internal/idp/providers/jwt/session_test.go index ae79ed22e..3a8210aec 100644 --- a/internal/idp/providers/jwt/session_test.go +++ b/internal/idp/providers/jwt/session_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/go-jose/go-jose/v3" "github.com/golang/mock/gomock" "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/idp/providers/oauth/oauth2.go b/internal/idp/providers/oauth/oauth2.go index a31e9d4c2..ad8cbbd45 100644 --- a/internal/idp/providers/oauth/oauth2.go +++ b/internal/idp/providers/oauth/oauth2.go @@ -3,8 +3,8 @@ package oauth import ( "context" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/idp" diff --git a/internal/idp/providers/oauth/oauth2_test.go b/internal/idp/providers/oauth/oauth2_test.go index 5fdfbc218..814a7ac9c 100644 --- a/internal/idp/providers/oauth/oauth2_test.go +++ b/internal/idp/providers/oauth/oauth2_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rp" "golang.org/x/oauth2" "github.com/zitadel/zitadel/internal/idp" diff --git a/internal/idp/providers/oauth/session.go b/internal/idp/providers/oauth/session.go index eb9ab49ef..065ad3b21 100644 --- a/internal/idp/providers/oauth/session.go +++ b/internal/idp/providers/oauth/session.go @@ -5,9 +5,9 @@ import ( "errors" "net/http" - "github.com/zitadel/oidc/v2/pkg/client/rp" - httphelper "github.com/zitadel/oidc/v2/pkg/http" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) diff --git a/internal/idp/providers/oauth/session_test.go b/internal/idp/providers/oauth/session_test.go index 9901ea11c..ce6fa3f10 100644 --- a/internal/idp/providers/oauth/session_test.go +++ b/internal/idp/providers/oauth/session_test.go @@ -9,7 +9,7 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" diff --git a/internal/idp/providers/oidc/oidc.go b/internal/idp/providers/oidc/oidc.go index aab525548..30d4d11ab 100644 --- a/internal/idp/providers/oidc/oidc.go +++ b/internal/idp/providers/oidc/oidc.go @@ -3,8 +3,8 @@ package oidc import ( "context" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) @@ -100,7 +100,7 @@ func New(name, issuer, clientID, clientSecret, redirectURI string, scopes []stri for _, option := range options { option(provider) } - provider.RelyingParty, err = rp.NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI, setDefaultScope(scopes), provider.options...) + provider.RelyingParty, err = rp.NewRelyingPartyOIDC(context.TODO(), issuer, clientID, clientSecret, redirectURI, setDefaultScope(scopes), provider.options...) if err != nil { return nil, err } diff --git a/internal/idp/providers/oidc/oidc_test.go b/internal/idp/providers/oidc/oidc_test.go index bbe08155c..d510bf15c 100644 --- a/internal/idp/providers/oidc/oidc_test.go +++ b/internal/idp/providers/oidc/oidc_test.go @@ -7,8 +7,8 @@ import ( "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/zitadel/internal/idp" ) diff --git a/internal/idp/providers/oidc/session.go b/internal/idp/providers/oidc/session.go index 366e42643..bb44fb114 100644 --- a/internal/idp/providers/oidc/session.go +++ b/internal/idp/providers/oidc/session.go @@ -4,8 +4,8 @@ import ( "context" "errors" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/text/language" "github.com/zitadel/zitadel/internal/domain" @@ -38,7 +38,7 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) { return nil, err } } - info, err := rp.Userinfo( + info, err := rp.Userinfo[*oidc.UserInfo](ctx, s.Tokens.AccessToken, s.Tokens.TokenType, s.Tokens.IDTokenClaims.GetSubject(), diff --git a/internal/idp/providers/oidc/session_test.go b/internal/idp/providers/oidc/session_test.go index afaa35804..200dac8fc 100644 --- a/internal/idp/providers/oidc/session_test.go +++ b/internal/idp/providers/oidc/session_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/go-jose/go-jose/v3" "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" diff --git a/internal/integration/client.go b/internal/integration/client.go index 71bc9805c..98a577be1 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -9,7 +9,7 @@ import ( crewjam_saml "github.com/crewjam/saml" "github.com/stretchr/testify/require" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" "golang.org/x/text/language" "google.golang.org/grpc" diff --git a/internal/integration/integration.go b/internal/integration/integration.go index 3e270b1d7..61d7f9d90 100644 --- a/internal/integration/integration.go +++ b/internal/integration/integration.go @@ -19,8 +19,8 @@ import ( "github.com/spf13/viper" "github.com/zitadel/logging" - "github.com/zitadel/oidc/v2/pkg/client" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" diff --git a/internal/integration/oidc.go b/internal/integration/oidc.go index ad1a44de3..b6edcd3ae 100644 --- a/internal/integration/oidc.go +++ b/internal/integration/oidc.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/zitadel/oidc/v2/pkg/client" - "github.com/zitadel/oidc/v2/pkg/client/rp" - "github.com/zitadel/oidc/v2/pkg/client/rs" - "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" http_util "github.com/zitadel/zitadel/internal/api/http" oidc_internal "github.com/zitadel/zitadel/internal/api/oidc" @@ -83,8 +83,8 @@ func (s *Tester) CreateAPIClient(ctx context.Context, projectID string) (*manage }) } -func (s *Tester) CreateOIDCAuthRequest(clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { - provider, err := s.CreateRelyingParty(clientID, redirectURI, scope...) +func (s *Tester) CreateOIDCAuthRequest(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { + provider, err := s.CreateRelyingParty(ctx, clientID, redirectURI, scope...) if err != nil { return "", err } @@ -110,8 +110,8 @@ func (s *Tester) CreateOIDCAuthRequest(clientID, loginClient, redirectURI string return strings.TrimPrefix(loc.String(), prefixWithHost), nil } -func (s *Tester) CreateOIDCAuthRequestImplicit(clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { - provider, err := s.CreateRelyingParty(clientID, redirectURI, scope...) +func (s *Tester) CreateOIDCAuthRequestImplicit(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) { + provider, err := s.CreateRelyingParty(ctx, clientID, redirectURI, scope...) if err != nil { return "", err } @@ -146,12 +146,12 @@ func (s *Tester) OIDCIssuer() string { return http_util.BuildHTTP(s.Config.ExternalDomain, s.Config.Port, s.Config.ExternalSecure) } -func (s *Tester) CreateRelyingParty(clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) { +func (s *Tester) CreateRelyingParty(ctx context.Context, clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) { if len(scope) == 0 { scope = []string{oidc.ScopeOpenID} } loginClient := &http.Client{Transport: &loginRoundTripper{http.DefaultTransport}} - return rp.NewRelyingPartyOIDC(s.OIDCIssuer(), clientID, "", redirectURI, scope, rp.WithHTTPClient(loginClient)) + return rp.NewRelyingPartyOIDC(ctx, s.OIDCIssuer(), clientID, "", redirectURI, scope, rp.WithHTTPClient(loginClient)) } type loginRoundTripper struct { @@ -163,12 +163,12 @@ func (c *loginRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) return c.RoundTripper.RoundTrip(req) } -func (s *Tester) CreateResourceServer(keyFileData []byte) (rs.ResourceServer, error) { +func (s *Tester) CreateResourceServer(ctx context.Context, keyFileData []byte) (rs.ResourceServer, error) { keyFile, err := client.ConfigFromKeyFileData(keyFileData) if err != nil { return nil, err } - return rs.NewResourceServerJWTProfile(s.OIDCIssuer(), keyFile.ClientID, keyFile.KeyID, []byte(keyFile.Key)) + return rs.NewResourceServerJWTProfile(ctx, s.OIDCIssuer(), keyFile.ClientID, keyFile.KeyID, []byte(keyFile.Key)) } func GetRequest(url string, headers map[string]string) (*http.Request, error) { diff --git a/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl b/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl index bc0123c75..adb71c42f 100644 --- a/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl +++ b/internal/protoc/protoc-gen-zitadel/zitadel.pb.go.tmpl @@ -17,8 +17,8 @@ var {{.ServiceName}}_AuthMethods = authz.MethodMapping { } {{ range $m := .AuthContext}} -func (r *{{ $m.Name }}) OrganisationFromRequest() *middleware.Organisation { - return &middleware.Organisation{ +func (r *{{ $m.Name }}) OrganizationFromRequest() *middleware.Organization { + return &middleware.Organization{ ID: r{{$m.OrgMethod}}.GetOrgId(), Domain: r{{$m.OrgMethod}}.GetOrgDomain(), } diff --git a/internal/query/prepare_test.go b/internal/query/prepare_test.go index 242b38740..c9770c0dd 100644 --- a/internal/query/prepare_test.go +++ b/internal/query/prepare_test.go @@ -54,7 +54,7 @@ func assertPrepare(t *testing.T, prepareFunc, expectedObject interface{}, sqlExp } return isErr(err) } - object, ok, didScan := execScan(&database.DB{DB: client}, builder, scan, errCheck) + object, ok, didScan := execScan(t, &database.DB{DB: client}, builder, scan, errCheck) if !ok { t.Error(object) return false @@ -168,7 +168,7 @@ var ( selectBuilderType = reflect.TypeOf(sq.SelectBuilder{}) ) -func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (object interface{}, ok bool, didScan bool) { +func execScan(t testing.TB, client *database.DB, builder sq.SelectBuilder, scan interface{}, errCheck checkErr) (object interface{}, ok bool, didScan bool) { scanType := reflect.TypeOf(scan) err := validateScan(scanType) if err != nil { @@ -177,7 +177,7 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e stmt, args, err := builder.ToSql() if err != nil { - return fmt.Errorf("unexpeted error from sql builder: %w", err), false, false + return fmt.Errorf("unexpected error from sql builder: %w", err), false, false } //resultSet represents *sql.Row or *sql.Rows, @@ -199,6 +199,9 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e // if scan(*sql.Row)... } else if scanType.In(0).AssignableTo(rowType) { err = client.QueryRow(func(r *sql.Row) error { + if r.Err() != nil { + return r.Err() + } didScan = true res = reflect.ValueOf(scan).Call([]reflect.Value{reflect.ValueOf(r)}) if err, ok := res[1].Interface().(error); ok { @@ -213,6 +216,9 @@ func execScan(client *database.DB, builder sq.SelectBuilder, scan interface{}, e if err != nil { err, ok := errCheck(err) + if !ok { + t.Fatal(err) + } if didScan { return res[0].Interface(), ok, didScan } diff --git a/internal/query/projection/session.go b/internal/query/projection/session.go index 816969d66..5bfc5dd8c 100644 --- a/internal/query/projection/session.go +++ b/internal/query/projection/session.go @@ -14,27 +14,31 @@ import ( ) const ( - SessionsProjectionTable = "projections.sessions5" + SessionsProjectionTable = "projections.sessions6" - SessionColumnID = "id" - SessionColumnCreationDate = "creation_date" - SessionColumnChangeDate = "change_date" - SessionColumnSequence = "sequence" - SessionColumnState = "state" - SessionColumnResourceOwner = "resource_owner" - SessionColumnInstanceID = "instance_id" - SessionColumnCreator = "creator" - SessionColumnUserID = "user_id" - SessionColumnUserCheckedAt = "user_checked_at" - SessionColumnPasswordCheckedAt = "password_checked_at" - SessionColumnIntentCheckedAt = "intent_checked_at" - SessionColumnWebAuthNCheckedAt = "webauthn_checked_at" - SessionColumnWebAuthNUserVerified = "webauthn_user_verified" - SessionColumnTOTPCheckedAt = "totp_checked_at" - SessionColumnOTPSMSCheckedAt = "otp_sms_checked_at" - SessionColumnOTPEmailCheckedAt = "otp_email_checked_at" - SessionColumnMetadata = "metadata" - SessionColumnTokenID = "token_id" + SessionColumnID = "id" + SessionColumnCreationDate = "creation_date" + SessionColumnChangeDate = "change_date" + SessionColumnSequence = "sequence" + SessionColumnState = "state" + SessionColumnResourceOwner = "resource_owner" + SessionColumnInstanceID = "instance_id" + SessionColumnCreator = "creator" + SessionColumnUserID = "user_id" + SessionColumnUserCheckedAt = "user_checked_at" + SessionColumnPasswordCheckedAt = "password_checked_at" + SessionColumnIntentCheckedAt = "intent_checked_at" + SessionColumnWebAuthNCheckedAt = "webauthn_checked_at" + SessionColumnWebAuthNUserVerified = "webauthn_user_verified" + SessionColumnTOTPCheckedAt = "totp_checked_at" + SessionColumnOTPSMSCheckedAt = "otp_sms_checked_at" + SessionColumnOTPEmailCheckedAt = "otp_email_checked_at" + SessionColumnMetadata = "metadata" + SessionColumnTokenID = "token_id" + SessionColumnUserAgentFingerprintID = "user_agent_fingerprint_id" + SessionColumnUserAgentIP = "user_agent_ip" + SessionColumnUserAgentDescription = "user_agent_description" + SessionColumnUserAgentHeader = "user_agent_header" ) type sessionProjection struct{} @@ -69,8 +73,16 @@ func (*sessionProjection) Init() *old_handler.Check { handler.NewColumn(SessionColumnOTPEmailCheckedAt, handler.ColumnTypeTimestamp, handler.Nullable()), handler.NewColumn(SessionColumnMetadata, handler.ColumnTypeJSONB, handler.Nullable()), handler.NewColumn(SessionColumnTokenID, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentFingerprintID, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentIP, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentDescription, handler.ColumnTypeText, handler.Nullable()), + handler.NewColumn(SessionColumnUserAgentHeader, handler.ColumnTypeJSONB, handler.Nullable()), }, handler.NewPrimaryKey(SessionColumnInstanceID, SessionColumnID), + handler.WithIndex(handler.NewIndex( + SessionColumnUserAgentFingerprintID+"_idx", + []string{SessionColumnUserAgentFingerprintID}, + )), ), ) } @@ -153,19 +165,35 @@ func (p *sessionProjection) reduceSessionAdded(event eventstore.Event) (*handler return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", session.AddedType) } - return handler.NewCreateStatement( - e, - []handler.Column{ - handler.NewCol(SessionColumnID, e.Aggregate().ID), - handler.NewCol(SessionColumnInstanceID, e.Aggregate().InstanceID), - handler.NewCol(SessionColumnCreationDate, e.CreationDate()), - handler.NewCol(SessionColumnChangeDate, e.CreationDate()), - handler.NewCol(SessionColumnResourceOwner, e.Aggregate().ResourceOwner), - handler.NewCol(SessionColumnState, domain.SessionStateActive), - handler.NewCol(SessionColumnSequence, e.Sequence()), - handler.NewCol(SessionColumnCreator, e.User), - }, - ), nil + cols := make([]handler.Column, 0, 12) + cols = append(cols, + handler.NewCol(SessionColumnID, e.Aggregate().ID), + handler.NewCol(SessionColumnInstanceID, e.Aggregate().InstanceID), + handler.NewCol(SessionColumnCreationDate, e.CreationDate()), + handler.NewCol(SessionColumnChangeDate, e.CreationDate()), + handler.NewCol(SessionColumnResourceOwner, e.Aggregate().ResourceOwner), + handler.NewCol(SessionColumnState, domain.SessionStateActive), + handler.NewCol(SessionColumnSequence, e.Sequence()), + handler.NewCol(SessionColumnCreator, e.User), + ) + if e.UserAgent != nil { + cols = append(cols, + handler.NewCol(SessionColumnUserAgentFingerprintID, e.UserAgent.FingerprintID), + handler.NewCol(SessionColumnUserAgentDescription, e.UserAgent.Description), + ) + if e.UserAgent.IP != nil { + cols = append(cols, + handler.NewCol(SessionColumnUserAgentIP, e.UserAgent.IP.String()), + ) + } + if e.UserAgent.Header != nil { + cols = append(cols, + handler.NewJSONCol(SessionColumnUserAgentHeader, e.UserAgent.Header), + ) + } + } + + return handler.NewCreateStatement(e, cols), nil } func (p *sessionProjection) reduceUserChecked(event eventstore.Event) (*handler.Statement, error) { diff --git a/internal/query/projection/session_test.go b/internal/query/projection/session_test.go index 607b028ff..b27b44399 100644 --- a/internal/query/projection/session_test.go +++ b/internal/query/projection/session_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + "github.com/muhlemmer/gu" + "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -30,7 +32,15 @@ func TestSessionProjection_reduces(t *testing.T) { session.AddedType, session.AggregateType, []byte(`{ - "domain": "domain" + "domain": "domain", + "user_agent": { + "fingerprint_id": "fp1", + "ip": "1.2.3.4", + "description": "firefox", + "header": { + "foo": ["bar"] + } + } }`), ), session.AddedEventMapper), }, @@ -41,7 +51,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.sessions5 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.sessions6 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator, user_agent_fingerprint_id, user_agent_description, user_agent_ip, user_agent_header) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -51,6 +61,10 @@ func TestSessionProjection_reduces(t *testing.T) { domain.SessionStateActive, uint64(15), "editor-user", + gu.Ptr("fp1"), + gu.Ptr("firefox"), + "1.2.3.4", + []byte(`{"foo":["bar"]}`), }, }, }, @@ -76,7 +90,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -108,7 +122,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -140,7 +154,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -172,7 +186,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -203,7 +217,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -234,7 +248,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -267,7 +281,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.sessions6 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -298,7 +312,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions5 WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.sessions6 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -325,7 +339,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sessions5 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.sessions6 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -355,7 +369,7 @@ func TestSessionProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sessions5 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)", + expectedStmt: "UPDATE projections.sessions6 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)", expectedArgs: []interface{}{ nil, "agg-id", diff --git a/internal/query/session.go b/internal/query/session.go index bcc87a303..c2921d235 100644 --- a/internal/query/session.go +++ b/internal/query/session.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" errs "errors" + "net" + "net/http" "time" sq "github.com/Masterminds/squirrel" @@ -41,6 +43,7 @@ type Session struct { OTPSMSFactor SessionOTPFactor OTPEmailFactor SessionOTPFactor Metadata map[string][]byte + UserAgent domain.UserAgent } type SessionUserFactor struct { @@ -166,6 +169,22 @@ var ( name: projection.SessionColumnTokenID, table: sessionsTable, } + SessionColumnUserAgentFingerprintID = Column{ + name: projection.SessionColumnUserAgentFingerprintID, + table: sessionsTable, + } + SessionColumnUserAgentIP = Column{ + name: projection.SessionColumnUserAgentIP, + table: sessionsTable, + } + SessionColumnUserAgentDescription = Column{ + name: projection.SessionColumnUserAgentDescription, + table: sessionsTable, + } + SessionColumnUserAgentHeader = Column{ + name: projection.SessionColumnUserAgentHeader, + table: sessionsTable, + } ) func (q *Queries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string) (session *Session, err error) { @@ -265,6 +284,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil SessionColumnOTPEmailCheckedAt.identifier(), SessionColumnMetadata.identifier(), SessionColumnToken.identifier(), + SessionColumnUserAgentFingerprintID.identifier(), + SessionColumnUserAgentIP.identifier(), + SessionColumnUserAgentDescription.identifier(), + SessionColumnUserAgentHeader.identifier(), ).From(sessionsTable.identifier()). LeftJoin(join(LoginNameUserIDCol, SessionColumnUserID)). LeftJoin(join(HumanUserIDCol, SessionColumnUserID)). @@ -287,6 +310,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil otpEmailCheckedAt sql.NullTime metadata database.Map[[]byte] token sql.NullString + userAgentIP sql.NullString + userAgentHeader database.Map[[]string] ) err := row.Scan( @@ -311,6 +336,10 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil &otpEmailCheckedAt, &metadata, &token, + &session.UserAgent.FingerprintID, + &userAgentIP, + &session.UserAgent.Description, + &userAgentHeader, ) if err != nil { @@ -333,7 +362,11 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil session.OTPSMSFactor.OTPCheckedAt = otpSMSCheckedAt.Time session.OTPEmailFactor.OTPCheckedAt = otpEmailCheckedAt.Time session.Metadata = metadata + session.UserAgent.Header = http.Header(userAgentHeader) + if userAgentIP.Valid { + session.UserAgent.IP = net.ParseIP(userAgentIP.String) + } return session, token.String, nil } } diff --git a/internal/query/sessions_test.go b/internal/query/sessions_test.go index fa5209bdd..1bae095ec 100644 --- a/internal/query/sessions_test.go +++ b/internal/query/sessions_test.go @@ -6,10 +6,13 @@ import ( "database/sql/driver" "errors" "fmt" + "net" + "net/http" "regexp" "testing" sq "github.com/Masterminds/squirrel" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/require" "github.com/zitadel/zitadel/internal/domain" @@ -17,57 +20,61 @@ import ( ) var ( - expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` + - ` projections.sessions5.creation_date,` + - ` projections.sessions5.change_date,` + - ` projections.sessions5.sequence,` + - ` projections.sessions5.state,` + - ` projections.sessions5.resource_owner,` + - ` projections.sessions5.creator,` + - ` projections.sessions5.user_id,` + - ` projections.sessions5.user_checked_at,` + + expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions6.id,` + + ` projections.sessions6.creation_date,` + + ` projections.sessions6.change_date,` + + ` projections.sessions6.sequence,` + + ` projections.sessions6.state,` + + ` projections.sessions6.resource_owner,` + + ` projections.sessions6.creator,` + + ` projections.sessions6.user_id,` + + ` projections.sessions6.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + ` projections.users8.resource_owner,` + - ` projections.sessions5.password_checked_at,` + - ` projections.sessions5.intent_checked_at,` + - ` projections.sessions5.webauthn_checked_at,` + - ` projections.sessions5.webauthn_user_verified,` + - ` projections.sessions5.totp_checked_at,` + - ` projections.sessions5.otp_sms_checked_at,` + - ` projections.sessions5.otp_email_checked_at,` + - ` projections.sessions5.metadata,` + - ` projections.sessions5.token_id` + - ` FROM projections.sessions5` + - ` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` + - ` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` + + ` projections.sessions6.password_checked_at,` + + ` projections.sessions6.intent_checked_at,` + + ` projections.sessions6.webauthn_checked_at,` + + ` projections.sessions6.webauthn_user_verified,` + + ` projections.sessions6.totp_checked_at,` + + ` projections.sessions6.otp_sms_checked_at,` + + ` projections.sessions6.otp_email_checked_at,` + + ` projections.sessions6.metadata,` + + ` projections.sessions6.token_id,` + + ` projections.sessions6.user_agent_fingerprint_id,` + + ` projections.sessions6.user_agent_ip,` + + ` projections.sessions6.user_agent_description,` + + ` projections.sessions6.user_agent_header` + + ` FROM projections.sessions6` + + ` LEFT JOIN projections.login_names2 ON projections.sessions6.user_id = projections.login_names2.user_id AND projections.sessions6.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions6.user_id = projections.users8_humans.user_id AND projections.sessions6.instance_id = projections.users8_humans.instance_id` + + ` LEFT JOIN projections.users8 ON projections.sessions6.user_id = projections.users8.id AND projections.sessions6.instance_id = projections.users8.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` + - ` projections.sessions5.creation_date,` + - ` projections.sessions5.change_date,` + - ` projections.sessions5.sequence,` + - ` projections.sessions5.state,` + - ` projections.sessions5.resource_owner,` + - ` projections.sessions5.creator,` + - ` projections.sessions5.user_id,` + - ` projections.sessions5.user_checked_at,` + + expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions6.id,` + + ` projections.sessions6.creation_date,` + + ` projections.sessions6.change_date,` + + ` projections.sessions6.sequence,` + + ` projections.sessions6.state,` + + ` projections.sessions6.resource_owner,` + + ` projections.sessions6.creator,` + + ` projections.sessions6.user_id,` + + ` projections.sessions6.user_checked_at,` + ` projections.login_names2.login_name,` + ` projections.users8_humans.display_name,` + ` projections.users8.resource_owner,` + - ` projections.sessions5.password_checked_at,` + - ` projections.sessions5.intent_checked_at,` + - ` projections.sessions5.webauthn_checked_at,` + - ` projections.sessions5.webauthn_user_verified,` + - ` projections.sessions5.totp_checked_at,` + - ` projections.sessions5.otp_sms_checked_at,` + - ` projections.sessions5.otp_email_checked_at,` + - ` projections.sessions5.metadata,` + + ` projections.sessions6.password_checked_at,` + + ` projections.sessions6.intent_checked_at,` + + ` projections.sessions6.webauthn_checked_at,` + + ` projections.sessions6.webauthn_user_verified,` + + ` projections.sessions6.totp_checked_at,` + + ` projections.sessions6.otp_sms_checked_at,` + + ` projections.sessions6.otp_email_checked_at,` + + ` projections.sessions6.metadata,` + ` COUNT(*) OVER ()` + - ` FROM projections.sessions5` + - ` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` + - ` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` + - ` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` + + ` FROM projections.sessions6` + + ` LEFT JOIN projections.login_names2 ON projections.sessions6.user_id = projections.login_names2.user_id AND projections.sessions6.instance_id = projections.login_names2.instance_id` + + ` LEFT JOIN projections.users8_humans ON projections.sessions6.user_id = projections.users8_humans.user_id AND projections.sessions6.instance_id = projections.users8_humans.instance_id` + + ` LEFT JOIN projections.users8 ON projections.sessions6.user_id = projections.users8.id AND projections.sessions6.instance_id = projections.users8.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) sessionCols = []string{ @@ -92,6 +99,10 @@ var ( "otp_email_checked_at", "metadata", "token", + "user_agent_fingerprint_id", + "user_agent_ip", + "user_agent_description", + "user_agent_header", } sessionsCols = []string{ @@ -443,6 +454,10 @@ func Test_SessionPrepare(t *testing.T) { testNow, []byte(`{"key": "dmFsdWU="}`), "tokenID", + "fingerPrintID", + "1.2.3.4", + "agentDescription", + []byte(`{"foo":["foo","bar"]}`), }, ), }, @@ -483,6 +498,12 @@ func Test_SessionPrepare(t *testing.T) { Metadata: map[string][]byte{ "key": []byte("value"), }, + UserAgent: domain.UserAgent{ + FingerprintID: gu.Ptr("fingerPrintID"), + IP: net.IPv4(1, 2, 3, 4), + Description: gu.Ptr("agentDescription"), + Header: http.Header{"foo": []string{"foo", "bar"}}, + }, }, }, { diff --git a/internal/repository/session/session.go b/internal/repository/session/session.go index d7b8bd76b..83d905ea8 100644 --- a/internal/repository/session/session.go +++ b/internal/repository/session/session.go @@ -33,6 +33,7 @@ const ( type AddedEvent struct { eventstore.BaseEvent `json:"-"` + UserAgent *domain.UserAgent `json:"user_agent,omitempty"` } func (e *AddedEvent) Payload() interface{} { @@ -45,6 +46,7 @@ func (e *AddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { func NewAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, + userAgent *domain.UserAgent, ) *AddedEvent { return &AddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -52,6 +54,7 @@ func NewAddedEvent(ctx context.Context, aggregate, AddedType, ), + UserAgent: userAgent, } } diff --git a/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go b/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go new file mode 100644 index 000000000..fe2509bd9 --- /dev/null +++ b/pkg/grpc/user/v2beta/user_service_org.pb.zitadel.go @@ -0,0 +1,12 @@ +package user + +import "github.com/zitadel/zitadel/internal/api/grpc/server/middleware" + +// OrganisationFromRequest implements deprecated [middleware.OrganisationFromRequest] interface. +// it will be removed before going GA (https://github.com/zitadel/zitadel/issues/6718) +func (r *AddHumanUserRequest) OrganisationFromRequest() *middleware.Organization { + return &middleware.Organization{ + ID: r.GetOrganisation().GetOrgId(), + Domain: r.GetOrganisation().GetOrgDomain(), + } +} diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 620936f12..c1f32616b 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -3763,7 +3763,7 @@ service AdminService { // Activates the "LoginDefaultOrg" feature by setting the flag to "true" // This is irreversible! - // Once activated, the login UI will use the settings of the default org (and not from the instance) if not organisation context is set + // Once activated, the login UI will use the settings of the default org (and not from the instance) if not organization context is set rpc ActivateFeatureLoginDefaultOrg(ActivateFeatureLoginDefaultOrgRequest) returns (ActivateFeatureLoginDefaultOrgResponse) { option (google.api.http) = { put: "/features/login_default_org" @@ -4887,7 +4887,7 @@ message AddGenericOAuthProviderRequest { } ]; string client_secret = 3 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret generated by the identity provider"; @@ -4954,7 +4954,7 @@ message UpdateGenericOAuthProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 4 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret will only be updated if provided"; @@ -5025,7 +5025,7 @@ message AddGenericOIDCProviderRequest { } ]; string client_secret = 4 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "secret generated by the identity provider" @@ -5076,7 +5076,7 @@ message UpdateGenericOIDCProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 5 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "client secret will only be updated if provided"; @@ -6837,7 +6837,7 @@ message SetDefaultVerifyEmailMessageTextRequest { string text = 6 [ (validate.rules).string = {max_bytes: 40000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" + example: "\"A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" max_length: 10000; } ]; diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 6770f7742..3c485f7a9 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -1255,7 +1255,7 @@ service ManagementService { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { summary: "Remove Multi-Factor OTP"; - description: "Remove the configured One-Time-Password (OTP) as a factor from the user. OTP is an authentication app, like Authy or Google/Microsoft Authenticator.." + description: "Remove the configured One-Time-Password (OTP) as a factor from the user. OTP is an authentication app, like Authy or Google/Microsoft Authenticator." tags: "Users"; tags: "User Human"; responses: { @@ -1306,6 +1306,68 @@ service ManagementService { }; } + rpc RemoveHumanAuthFactorOTPSMS(RemoveHumanAuthFactorOTPSMSRequest) returns (RemoveHumanAuthFactorOTPSMSResponse) { + option (google.api.http) = { + delete: "/users/{user_id}/auth_factors/otp_sms" + }; + + option (zitadel.v1.auth_option) = { + permission: "user.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Remove Multi-Factor OTP SMS"; + description: "Remove the configured One-Time-Password (OTP) SMS as a factor from the user. As only one OTP SMS per user is allowed, the user will not have OTP SMS as a second-factor afterward." + tags: "Users"; + tags: "User Human"; + responses: { + key: "200" + value: { + description: "OK"; + } + }; + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get a user from another organization include the header. Make sure the requesting user has permission in the requested organization."; + type: STRING, + required: false; + }; + }; + }; + } + + rpc RemoveHumanAuthFactorOTPEmail(RemoveHumanAuthFactorOTPEmailRequest) returns (RemoveHumanAuthFactorOTPEmailResponse) { + option (google.api.http) = { + delete: "/users/{user_id}/auth_factors/otp_email" + }; + + option (zitadel.v1.auth_option) = { + permission: "user.write" + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Remove Multi-Factor OTP SMS"; + description: "Remove the configured One-Time-Password (OTP) Email as a factor from the user. As only one OTP Email per user is allowed, the user will not have OTP Email as a second-factor afterward." + tags: "Users"; + tags: "User Human"; + responses: { + key: "200" + value: { + description: "OK"; + } + }; + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get a user from another organization include the header. Make sure the requesting user has permission in the requested organization."; + type: STRING, + required: false; + }; + }; + }; + } + rpc ListHumanPasswordless(ListHumanPasswordlessRequest) returns (ListHumanPasswordlessResponse) { option (google.api.http) = { post: "/users/{user_id}/passwordless/_search" @@ -6806,7 +6868,7 @@ service ManagementService { }; } - // Add a new Azure AD identity provider in the organisation + // Add a new Azure AD identity provider in the organization rpc AddAzureADProvider(AddAzureADProviderRequest) returns (AddAzureADProviderResponse) { option (google.api.http) = { post: "/idps/azure" @@ -6824,7 +6886,7 @@ service ManagementService { }; } - // Change an existing Azure AD identity provider in the organisation + // Change an existing Azure AD identity provider in the organization rpc UpdateAzureADProvider(UpdateAzureADProviderRequest) returns (UpdateAzureADProviderResponse) { option (google.api.http) = { put: "/idps/azure/{id}" @@ -8246,6 +8308,22 @@ message RemoveHumanAuthFactorU2FResponse { zitadel.v1.ObjectDetails details = 1; } +message RemoveHumanAuthFactorOTPSMSRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message RemoveHumanAuthFactorOTPSMSResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message RemoveHumanAuthFactorOTPEmailRequest { + string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message RemoveHumanAuthFactorOTPEmailResponse { + zitadel.v1.ObjectDetails details = 1; +} + message ListHumanPasswordlessRequest { string user_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; } @@ -10914,7 +10992,7 @@ message SetCustomVerifyEmailMessageTextRequest { string text = 6 [ (validate.rules).string = {max_bytes: 40000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" + example: "\"A new email has been added. Please use the button below to verify your email. (Code {{.Code}}) If you didn't add a new email, please ignore this email.\"" max_length: 10000; } ]; @@ -11759,7 +11837,7 @@ message AddGenericOAuthProviderRequest { } ]; string client_secret = 3 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret generated by the identity provider"; @@ -11826,7 +11904,7 @@ message UpdateGenericOAuthProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 4 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"client-secret\""; description: "Client secret will only be updated if provided"; @@ -11897,7 +11975,7 @@ message AddGenericOIDCProviderRequest { } ]; string client_secret = 4 [ - (validate.rules).string = {min_len: 1, max_len: 200}, + (validate.rules).string = {min_len: 1, max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "secret generated by the identity provider" @@ -11948,7 +12026,7 @@ message UpdateGenericOIDCProviderRequest { ]; // client_secret will only be updated if provided string client_secret = 5 [ - (validate.rules).string = {max_len: 200}, + (validate.rules).string = {max_len: 1000}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"secret\""; description: "client secret will only be updated if provided"; diff --git a/proto/zitadel/object/v2beta/object.proto b/proto/zitadel/object/v2beta/object.proto index 75f82d98a..c7c68f31c 100644 --- a/proto/zitadel/object/v2beta/object.proto +++ b/proto/zitadel/object/v2beta/object.proto @@ -8,6 +8,7 @@ import "google/protobuf/timestamp.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "validate/validate.proto"; +// Deprecated: use Organization message Organisation { oneof org { string org_id = 1; @@ -15,6 +16,13 @@ message Organisation { } } +message Organization { + oneof org { + string org_id = 1; + string org_domain = 2; + } +} + message RequestContext { oneof resource_owner { string org_id = 1; diff --git a/proto/zitadel/project.proto b/proto/zitadel/project.proto index e050ba3df..b487a2dd3 100644 --- a/proto/zitadel/project.proto +++ b/proto/zitadel/project.proto @@ -48,7 +48,7 @@ message GrantedProject { ]; string granted_org_name = 3 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"Some Organisation\"" + example: "\"Some Organization\"" } ]; repeated string granted_role_keys = 4 [ diff --git a/proto/zitadel/session/v2beta/session.proto b/proto/zitadel/session/v2beta/session.proto index ddbe14336..08ef97e21 100644 --- a/proto/zitadel/session/v2beta/session.proto +++ b/proto/zitadel/session/v2beta/session.proto @@ -39,6 +39,7 @@ message Session { description: "\"custom key value list\""; } ]; + UserAgent user_agent = 7; } message Factors { @@ -72,9 +73,15 @@ message UserFactor { description: "\"display name of the checked user\""; } ]; + // deprecated: use organization_id, will be remove before GA (https://github.com/zitadel/zitadel/issues/6718) string organisation_id = 5 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - description: "\"organisation id of the checked user\""; + description: "\"organization id of the checked user; deprecated: use organization_id\""; + } + ]; + string organization_id = 6 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "\"organization id of the checked user\""; } ]; } @@ -131,3 +138,18 @@ message SearchQuery { message IDsQuery { repeated string ids = 1; } + +message UserAgent { + optional string fingerprint_id = 1; + optional string ip = 2; + optional string description = 3; + + // A header may have multiple values. + // In Go, headers are defined + // as map[string][]string, but protobuf + // doesn't allow this scheme. + message HeaderValues { + repeated string values = 1; + } + map header = 4; +} \ No newline at end of file diff --git a/proto/zitadel/session/v2beta/session_service.proto b/proto/zitadel/session/v2beta/session_service.proto index 745c43001..718a29381 100644 --- a/proto/zitadel/session/v2beta/session_service.proto +++ b/proto/zitadel/session/v2beta/session_service.proto @@ -274,6 +274,7 @@ message CreateSessionRequest{ } ]; RequestChallenges challenges = 3; + UserAgent user_agent = 4; } message CreateSessionResponse{ diff --git a/proto/zitadel/user/v2beta/user_service.proto b/proto/zitadel/user/v2beta/user_service.proto index 01abeceed..36517dc4c 100644 --- a/proto/zitadel/user/v2beta/user_service.proto +++ b/proto/zitadel/user/v2beta/user_service.proto @@ -119,7 +119,7 @@ service UserService { option (zitadel.protoc_gen_zitadel.v2.options) = { auth_option: { permission: "user.write" - org_field: "organisation" + org_field: "organization" } http_response: { success_code: 201 @@ -655,7 +655,13 @@ message AddHumanUserRequest{ example: "\"minnie-mouse\""; } ]; - zitadel.object.v2beta.Organisation organisation = 3; + // deprecated: use organization (if both are set, organization will take precedence) + zitadel.object.v2beta.Organisation organisation = 3 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "deprecated: use organization (if both are set, organization will take precedence)" + } + ]; + zitadel.object.v2beta.Organization organization = 11; SetHumanProfile profile = 4 [ (validate.rules).message.required = true, (google.api.field_behavior) = REQUIRED

KJPRB&C&Y`YKL2!A%+`O_d#XRA_qTJvce@@DVbQP~lX2dl-Ui^O?m zf!0gbcKQo<3{4fC$cIP*);JVfPor~VGj{I)I3^IR+Hn5<3^PdAN^L6M8X4$l$1fe- zw+QoNs>e1eFrXBE!!S+uLflbWL~(bitoOCu>LzM_sJ9$;BkmrtI8?1%f%sh#oivEW zQDHLaz3tRCzOnz{ZErd>$at{lg^Y?~n}NFEiM{37rJoLbtSUv^CuWeSET@ORMqbIO zz$2!zXgpLLcTdcCb}B>CeS9HiPqi$Ma-0()a%b&xhCje6PWr~{byKM4*^tA};wQ5k ziQW%KIA?}UC!+Sx3?@XDcrSM~(@(OAppfT%kEf;yw4U7J@fbZ3$jZRx&ZTP`Zrnq| zIY#4&CT+GKbUGf_Sz{VKlf*Ah@S!VnGG4dhcWM{0q~v(opHibuIw9IFbiR}!-l(o4j!k8 zsC`MzFtBUk#}oVIRijnPi5cgG=FHQ}ocW&(Umgh@e6i+iI`1k3u_-W!8agEGe=c%_ z%S$a8NLrt$o>=0dTkj(=8$D-H{lb_3Lx%tBLiLWhr|)~TUf&hUp~%r5cyg7H{V>XD z0UeUfL!JAh86~P*C_ayE>x>ql74#Z<^^a2NW%Cxv6BO($q)?_%8`NN5hOu zI0h)+t2`h`wlo?FfgvA#_>N&+SUxyairZ+;%4%xq<3j+l&Dreo^Y$UyOhvFdeVh4v zk(%o}o%Rq|{4_3P%*5Y3@E-g=xV-CVUQm}}J}?E!Bo+7U{`8%tu{>*+#PMHK_c&7q z^zjt>VXuEz|M0Yv{m)H3UMWrfg&$RLWFye2ImQN^c?wk@{RPbq`RgGCP@fJ|qj2zoK`{omVyN z+cGDji+!t8_58&P{-YqdIrnS}7@4m7@k?%S(7lBFoilM9X`KNItNp>-yE({ptjbTT zmbox#C25rVDAbH7JuyctNU?;UNBAVf1AbTyU(jm>Xp2>=r3)FK`xyQUOI(vGvR%u% zhER;1c9_Y-18@kSKn1N+f5_}*NFF~~4IMkq-|pQER=^^Ir&iGk9>mZUQL8{B-X94` z8q1qwnHsTX4lYcG?w^ZEMuHopfc&pQTzNOp)`Z*7&N5lEI0o(4ZNZIz&z$avtTj*$ z8}GP{RnoXpBP#{be&cchlcC866+;Z%2Fv4<^!c zO~xDx;dnxq%i+Fv=>r_9Nf-<$PCxZAvkcL*^mK6FN?`aG?1 zZ@#D9XdfTk+who8r4~jm*isW-h5_t+y|!MvBM*BI zYc5uAqU~3ugT$DQt?@>RUQ2=HaPfJ?p$R2H*zd&|!7ug5{o2Fc)K=U5FsWfNsW2AvYyg9qa^dQdS}UU>6;20qZ;+O?q6Lgke!ip)iJMJhx%YEwLRfc0&Bk(Hm2^=Gi+` z=)My{N8tMVtm4aF zHLvL1&adn4YSP)+A**?Ub9dBqLN}zDU?R0B zYN_OTbIC-Sd$l1*xf`*Ho7>5A4u`F1tle!ge5(&M64s(xM)w(nz=YN3=1jbG3E#wqe3JUPk_+zQ?hj>0 zJZ=BIXy&rTqxl3FvNvnDWsspU^gke=r^*`ev6%mWBQC_jBZSC z6wOfSL$16rR;J-U6}{Z3I31Vy+$`}um>5;Ve3R6Lo(?FL^9Ff5b0;~&q5B^!fSn)Nor190 zkXqx^{fn*vrEjtAk4LJSxS54P9%;*mW5xBv8c)MVs3ToXkt+BcU{AL}nUr}z#~|Xn z*VSJJgOe%iYT;6+4d&%?KMn&KbzL~&6;Dzel+I>xW-BsIIN+>m&rM>gH`}?lhw=Hj zUf-ctDM#-;Ks@5bE#)_VI=%TSr?!I#AGY<4-R@A|XW#O1TU9!ps!PW4Ze*6}#%^1U z1?-2ylGfybr7Y_JLbX;j=DrP{m*MkJG3=KV;KnyXD1;RiD)^Q0&L5q_zSKE|M^`!r z(oQTqRVooo)5{r)a&aeYk5HZ{l!o&y?a(5f`E4?S1D{1jB@N3t zFQ(6Wel&vNI1vA>t#4AB*r=mR?&=#T?IlaQ3y3SLo95H)1j_m={5I!jm1W^8klL3U z&MRXr2IK3Z>r}O6YiyWekC4gV!RKbY$HxjbOGvQNtRik{rF8VP@Oe*?1OhD*>S~DA z&R-DnuYIh|wo9Ixk%<>cQ%D{I+#ycB%Ac#v0<7PxwBBlV*HeWMi&Y2%HF zNM4`0<{c}QnWtDw`L9=Y!*tyX=Y=;7TG>`QF@YCE?Ssz5p6ycj>Z!cnC*lz#?R|KA z?E;({`R~bPL%)yF*=w=BJDi}<>&U&_p!lS*jE}=qS`gzh>V^fy$@)Xk^QpckI z`Hh^V4SDtX{ImN^+ea(`mBof+W#ft}8wV$lw+ED)CO7DjfLrXnd%hXOaA3&O?aKhJ z1`Cml!!Sz9B`)vgx_XIu=J;CU6@*_7Yl|(9?Gpl*k1G-F6*nC`p77O6{orlkuRY@! zRL}HeN!G~!D1)*)d_mo@ud7kX#ifhmiOk{q8s6g=h1nK`39BHxTmS?>6kefVSCHot zj8uY;kW;s{FS0#^BMZPqfXn^kD8qcw3fQWTpRSn24%p6sf@Ri;g5#{fVD63*E5hY5 z?3tKm&@KEB@;XWoni`Rw4TYBh)2XB`mQG-!6{dmspA3QuPjGFwi7PtA<@M!^oQHVo z0a@4n8h4-Ls!vJZGu^!p;f413xt~|SwS1*jxm42KXE#-M&h}5dS#a&N$2wBo3sZ?N zjIs=y72SK<9nuMEJlc-F?vft|2aZ4|2%K|s%)d)!|<%jQ)+<0Bi9qnNq zSqPB?x&27ZB;f?xLP<^w;Z{>B-S628hp>LMKk3|aX0dT+hGr>LFd(tf5{l>Z3qOzc z{CWr9RkmNG%8SHBH(5w6Na0pT%&Yf;(P@0n&#U)QHJbxI@6F1gulkFWcR?xzI2XDI z>CABbCiiX*^U^1rZE=cM%+&C|<^!5PT9Br*-_b-wlX(7|@;ht?kKFEsYs=CeRFzCB zFll`kt2=0g4g^ai&j@yUpvl5^Zq0v|! zzlh98QkICid4}Kke9Z@yHUd*?@41qiLZ!f9Qa;o>$7t%Q`qYS2(qb88)nX`l{d8EH z5dY`BUDpPTbyw#+p_w>@$e$ew=Tm&EMzCkeXYou}o2t=_R$s|chw-5K{T?mEx8Re+ z@{F+_CP&dLD63RzCfdR0S;zO;c`dD&|G3Jo5S45 zENQTdXJWtV*0>wB0(HiOe-7;)b?@c=%bsW_Q%haWli}cV5?h+o^cH33*>c%LgMJgw z1dkt4xkPeq<6I32kps{DEp)%Ldy`&n>W_yu(CK3PqfQ1~;HyoWEHREbaVEW8jxB>Z zA<5t*=BG7r>1ev6x){Hc-&&|k_qdZq+=FT@4fbtgNFv4|udAzIwv~xV*gU%j-}|$M z(>7xgqpt_21twvm z*`7dbXGB5+M!_*dQ*AIfUfNQ>MNc3!u#1j-c;}A7>|@WP1l;tsMAbD8UcJ*V&%ZOh zA1ZQp@N|Q>fV&LaHu&_9ux)T3cetElme9!HYvyE~yS%;I22uWCucCCqx3emKW{ymf z%g{^M$n382wJ0}X^ULa*T~Zr}GX3hAJ7y8tzVnzuIf^QgY@3(cIEY-m6+2+_COQc} zsU&&*+{&)f#`m&zneF!!$r=`#beSnR*;`Y$8WQRI=x#K*fA~g#He1F@@WxK^(`8tu z3#ot-0n74gCl1F++SD!cTQ}EVW_&Filp`bZ^z)XeS~@$#*|k@haH(Rc%}xx%nBVZe z5opIf_3a*s*4kT_fKq;)=Xa|+DYAx^I_>ki=-^2TFzZbl!=Y*ID7dqQ{@tPJxyRqV zn|8>Lk2a0UC}H;8;=C~yBOSJ$hN&GvmGyQGRwj@kb#FO33(dFn$x?L@eNx<_klJa- z%V0jI&eH;ZK5dN<3#1zUvr;%1Hmf(Q|&>Q%yw-qPnZ`;vmawx4@EQrEY9S|t zON~6xPvbO0^8Os>m$6|3(22fVw4+W*mhS?6b@r>=v8aDKC*5O~;Ek#cs}{AJP4u?f zbG@nBOydv&wZ+CSo!yfvPuWLqejAOdjUR`t*`?!39w^eQ<$APh%w0d9qtZ>XTn>hV+Z5+R(zM#;ci+@9s0myhg=4)L zWQ^o(jlm4b9^+9ZqA@vb52=9En;pLgXm9d93?cPQIaA!5N@0FRJN2M-3yZ*{Zf8yc zE2E&BW^(4o*bfIT5>2eu^YY0E#FNV@O}&#RrZij8=7FN$^A94(_BEKin^NfOr2MvB$2-FQqc0CQ@2msvq&M_CEo2zuWo<4k5Hp=HZC&HqnT*>7^CP#0$AJK zgGbDR>lruHd|jj6K3TgFa5JVN$fuO)PkZmG{ zw)(+kv>yv+u^U8ie1QLH-d;Ir7iHB@j-^JJO&BopZPA$jW+abOV0JT%P!j*{c7@@U>A!tS$ofZr8{4$oGT5y!e zj|!?UMFst6)-!)z?ioCg*Xbygo>fuun$N~HVvhV3rXcZS(s6VO<-nNDtn(qmeQlKN zp9Ovdv?>zyNDq-m!M0e;a%@XIkIx4iDd~;LPffw-@Ng{&=f$57jrVw^E3{NnRGVvf zXGG6l_gQZQ9!gPXzt}0jA;AooG-+QhaYU9MZ*LquZ$sIFXm`I3+BD{qv~`+g0p(}E zgKI#^XnUrGdL}S6r2hC?DR2Ui8T?Hatnw-pAB=uT^#c%JVd}i$PZ^|=-1WT zo4RWvjdWm~GAv@rOJZ8#F`nc+;#PsEm|ki(iEgZ~Sw`Rp-{Oj-SYUrA))h_G_AY}K z-$>tJolP5BUUdpx%w%$fMOxu;lRZmReERJzKkUb9aaMBe3e_oMSl`%@p4@^43^ z;`I@X^VY0RXkzCtch@mY*ijVc{~6iw*!R z^>|NZjg&&pZ4QX-^v)@l@24XgHNS7=Oi&Qs-E%$;RctKL3_MvvsAMY!=y! z&&4YD2neRIiM4)!!<;_fF|(Awh54X_s$v2goexs}>h`W~-}|K!qRSP(IV6$adC@}( ztT7NTWMRfYa#Jh<_P&a~sj3%*#LBB1J8wHG2vm9PO-2|@CN25@;AD8K=et~o|MYld z)lU(z0_m(c(>JV~%^e&doDe~m9xd+tNRWzV``F;${nf_D4>!na84r%zMeV5SfjH-@ zF0L6+feRf!UPTsh&j5GJ4q>|jr z-^1Yg3A;@?EUL?lWe*VM-4AvG7#3QvO%57*YZZP@G_h{G2iNw$KJ#@crTRE=+n5`p zOezKH!5|g(am1}5R}ID~up7^ZN z_G5uQ{P$Oc6H`yhgUUMv8{o_CRm>I>K!-PvlC61N9@N8ok5&tMA%Fu?{|e@91V)JzHKNT;~u> zG+pOuEEe6J%gDaTwt1`Ie6yc!{Z!ldxe@P5fb6aVQT0Ov6u}V?z2p$V3_=gLy1&|m zzVn%Y)4T=Ah_zp!S4xiFN7i-FK?yT0E*6Y-nDeSl2TSlddRBo==$zqhxPbar@|u6f z2CH53i9>s;e;NPBUva!oq8#pTz5z zR1Wx9oh!xT_zrw4_gBjm>l&@B!vh!onzFhn;@VikFWIB@x;#kO`{XKH7=R>z6Es^2 z2NdY~D3xl}M>Fo%$i+t1*(`=1!syvMK3Yg{*2IIvRr@;yj(0^2EQsu7TO)C<#zc(d-}d0 zk7lF4wj}(ioJ{N4=8TlidNoG;2N4prm&dz{i$<-=q5#KepJa$0R3!d1PHrLL$Dg9uRWDlx7)L#R4dRDN|TauX)Fm_+-!Hb zIHOFWer-R$Vp@SAS~s4M_VQDs{RIa9DQ$Pn=BrqaO9a1PXxN-?pfmB(Xte(W0z$y# zQj=8aWXelD4Sjo-HDb;BUz3SU1&i38Bpm28GzwvfP^pScP9px2wE+5MUO18hy8j=Y z`I~l0|B9)23IGetcrE&wL1HbURPxdGT*M5uLTH?67EJ_oq|G!Tm+ea~n~gm9jW;cE zjU^sFG*{ie)IvgaLE1lX1^=yHSDytMfAKI2AnM(P7Z{j`I96IyfuYj7XroD~Ueowp~utbLTBeF`dEH zvV^q&JcvIY9a$@hf+&Dl(HU4~-R|bEinlcXg+mUq^nZ28o4D_PB-)1vPy@5eq;<~$ z$+)DdO~!{mTkW~4qa3#a#a)a`ixRNFF?`QtB*9#KQCCQ6wW#E{;-wm%pF6~f$1+I( z^`n`-$9@F~KuQcoHS?vV{C^DU5$0uH!J^SA;g`#C)y^jGq&4KSb2w_mF^cf1Y)4$a z(+U8^i47O(#`Dz3Gvw{Bm{ybLAHKUv_Xltyz;M5OWBFfi35f)6DHfj|em=z|Idu$x zx6*pl%0v@WdC(V};e0wk0Uy~ikc>(ugu1du;(Vx-=rD!55S-`Vh@8!_{|Bl-j1*+k)Q*#N>ey7YG0rlln zW9!Q_KipIRTZ7g5X}%>&Pk0s0(A-!><)YFtA|UC|@x^&b7Y8_^hW#8q7~RQh!{9$# zy+WJ^Jvap(ZY}EW%>RIT2oW#>!qMIl-JtR^6f2j+h&(w2aMHS+ePILfec!Qs0GNjd z(r)-Igq;=P~DTxO=uS^x>hj%6}q$7cnQ9=RSJZ$iw1BLR9^|p7_76zZ{ zBbOYtjn$c7pgG`0I`H86`+}eBu3q|A;dlqTylC{w6|qIYqL zflQecJ84v<0Ho8lu}pJ835C{-B$O20>DT}zWZDl!W#=YbY`9M@A&-B(e(TZ;VbFd1 zANV7rZi_%}B6>3mFB_4eP7YR)$wFu=hRT(@_%WxWV$KjL;oB=4&6eHxYa->sT8bJ6 zi@lP5xb(}jdrNv6?Ey-rT(JFD!pNXUo-;IJV}^O5)Re>D|7B+Wk4RH5;VO;;v|3)?o2pb<5J?4y?Z;qqGC~&2&-i%IYDrtj z@9v;Ul|=I&R3#%na4C!82OMBcl}!kbs9pT788`aD%zDV{#*&1MtyZ~EG1y7K5h@jWUM ztM&=0z&)^^ojLnpmArw5`9~bRpOc>o_cn~Xta%z)vDM7ZhkF4AnB?�&UfWG{~$E zF2cA}E!4RVeVUbLUa0L3XncNc1O%8p+yHwL6`xO-1TI)|>^SXXE76q-g%AVgEn!WY zUkgP8oZ^2LWE25rPVaJKd&_%>x~y_E>O>Q5E#iyT)H#>GrsynwRu5&@PjD|Pioi*g$bKj3JVPlN0gwVel>TVv z)PsgPtQ)*#yF|iux68Y%;Be*sK2oXr+mEL4Y@zcX^zY=E6>M**vd#R@iN_nq3=JwW zb^BPV`IUDPdkWJf;z`0lY($;Tn@IDyFt9?BkqNzhe~4KNvM@&@4@%hAJajRI5u5kO zMI54iB7ihX93IDQ%mHs1D^YWsOtq=~{*Lj&_nBfQJLlyON9V0+lH;15hB{0}W+V$- zWtoNqn?uNyrbX^69?`-1PB8SPiy@qO%&03~PlAP#;l$T_pRQjgxx#B*t|f7#Dv`ia z>05+q>b!03S!rCa>Rnac=haRIW4S^sq`V;FKL13a;Yb%(b_zT%ZkT0vCItXp zvTujn-8I~|2xk!%7<>yqY;-T&;M89i)0q2qKE6%DD9L6ts>7!JG?=%v_`}knS@RJR zb<}wNsW;75vXJapcxz`H*HG>P>2R58(ztB?TfvI8L4t7Z%|7zc(6~jUeUcCi{__{} zT_^Rc)1iNy_Tvy0&y^Ja@YmfP)#e)fIREiPfB7IFj0q6fsH!fGWPj)z7k8w{q?fV9 zWrnHgH7_wyV04)i-7*9)ymN;$)M7EcX{j&1Pv2s+=}2<<4q{1kcHRvzSO~U(`KCRJ zh6ao_4B%XJc;AVei_%bdA2nSuxowN`TLp*3Nees;>t*^(Q%}W|_^Y!8z9buDg`^a; zTtBmbCzT{8>(9puO}iy2RO4hpkYrIGVdp0?kS<@StVngGDjZD6v#3+ba&j_>jxt}( zGRaeVZ6mHU|8LYegX$C2ROX}$*AQYGt*Rr!Lm-VaH81}Uo^11R$Pj?_7md+aX#rSl z(u~S#89ygAu6I;!w2wlxjBCgnB{(GW)^cPobTmJ@C-~t3gsW4(Mi^#FI0v76O;IxU@)F3(aHXRhQgGXK5G5D22UA+}QEU`?1fsQj@nh|jnR zMPw@hj&omGnn780VR2b=>W>q&!FkCu^ zFDXJmfXh=s{azp4rSLnJN{x0RQ0u( zk3ms==KwxgpS5xO>cM)f7o_0K=#6xiK|+>b2IPLnN)scmLBHRMd3eT@E>#rRjg%8J0$daf+Pbc>6&z3!?!k!%h;ryJX28s+<%G`E#2~a3} zMu?Y8j_*UWez7`+a(a7@q;(79^=s<1UZ+LA4~I4xi+B;qA5cXmllBJPuuGwXwc*w!XfJ-vm)t{rafs(s#x z4NRGZbF5C{ztkp0CY6@Cb<|MIE^n5aiW=2xjRKL?(tdbV#_y$%z&;W*3o}j`sdb=1 zwokK->t7YeK+#A{fzQRrw%?1(n#RN3$;WwynVy*?(6I_}UKag=$7~aH8%2hcXyv@+ zvSmRs3|9MHKJnLs|Mj0+DCQ=U!I0=_0oJkRi;qepIBt>m63mgg7*ms3|n} zmLksly&PGHBts0qoBVeN|1}h$ zhXQ75ce8SEuIN7l^nJ9n9{(ZuUoyLFLAn4ci(A(E4GaEmz@z}^TdsJAUNPWb^jn?) z`tc)1Xjk;LK>xHkY~BCW@V9Wnl|eOg8I`?Nj^8||pa+j{TM3|C#~yJ^z&}sr&$q9h zm$`gid25yJuQEd>ox}kZVBI$FUUkP0@Tc-HXBhjM<{{q!`(sqO!|(ihz>N?eylxBi z-NAMJ2?6aNeu@6w{3d)a=zoH}>m~hu{-5S@!_6J^ z2SFnGk3P0sj$izz5#p78{BzuGW}pMN^GN-CehF0IDd^vV#af~1-+ha#fGudhDc||= zO8)wIq~(&#RN2q}VOM{?7QO-e5Tgmw`faZvXrOx)(*CP(*YFcl4YaR(Xm~X+x4W16 z;HiBbKS^6J^&#ikFwvDhh=RxC4EzhP)dxzzj}N6e_pkVY3f$FGc;Be&*eOE>+P|WX z9lO#8oJ)Nukf*}ArvK8Xm-=AZg?B~pZPASeR-aqfw03m`;4@>^)JUE?~Db-aMG z$-lwzx*9Ay7_4QH`Y_Ek+Dxhh=3$$AA-c}vD`AmBK>t)9s9#6_RRs7wGt0#Kw>NR?#0JhY=lC`wB8#l>3hE)cohY@wv=VM|Hsk&pVs{T zv^NXmQZik|fB<iwd?QBg{K zyGF9E0+PV87IaDqWv}S_T+;8yN&BtZ|F$hZpj(w+y_EQ`C^qAgmIsM7p1}^EVIo!bK?fAxM+eK7vi?~4Bx>hA_^qTu)V9C>Th-~C^`RP7-9 z;K{_ z1<++RnQbHdD}s-r+*b^IpdncUD{aenaA%VOFL3qOBIy-`v*d?JvnLYBX(H)qdW{P# zt~mNnAM%+4aK!(4jPakDB)_&G*Mm0~S$Va2N!0WW?Qtc$89<2!emV;X=0-ynr zzo^wRHD1#)%wEA|=BkjgyUqX$FP%3{cHpzCl>))73T)L4wLBda9v9v!OJ#Il@?Z`{ zNzN^ou1aUMDoNs`?Kg{igBGkJG%>QnG{I1Ijc zM@<=3t?b;=@N}r^GF^@BcLGr1EXlTe?9)znE``HJiO&hk=2kHOc}O0B{Y`Q__Hryc z_NljPlr(ajYx$O{H8eca#csJ@^r>UJJvp{m!lxH1PaTX@stYXYPewZT)c9LkV*TV_>UF>B~J0rC5mk-`b zcU$;sy8mno{8W=e-o?Ybl?lHp!Z&Xpy06@N#L4*(yl=+fP1I7@c2(#3$%aen2v!MG zx=F86tq~ezuQ6PT?}RM3A3gTE?s4 z!Uno(o8bySP;R?RWjD@L!C~h7)A*T4+iuLxX;_%O-tiQ5Pd@p}llcxkiPhs&Uk%!VMHGx35T%rMFv2 zn7c1`XNg*)6al(HfAL~jb(FsU3Js+2Du|ihxn?sV2CG^R(>1Nx;0ZpUT(0cOu`2A( zw)7@6KNRdb^Rxny$Q7@~Rn9;0@L82sADp*tvC(~phgx-Us&(<6uy8K>KG$7Hun7-* zY;-!EEmt@@pxbLR`F^-qUv_kDRo)7}B14;ib9T}|#wv=saBIRvH=;o0d@6(K?V{q5 z{al%O)Lz}@`~hAuNsjgR!{7U1 zQv7GMB+jQXA8K4eGMf%zl4+w}5#4k-#CcFPhZx!Pv;8r#b7p5{Q-2L5YK&;@jXoRv z_DGdTn|CfcqjxJZTA!~cGX|>|y&n9K;G7xn*jQjO^aZJHA0`)SRd@C^Hei&_0j|Bygql(Yn)+Ddmc58fOob$(=uL@jkZEPTDLhfgV_Fra&Yr8f7zz4 zzWof*_>PW<3_eYuxdYom`oj zX!7`h9F>DA)#2U2TiN1sHF9a~K}^=R>It{+^Nl|-tgx6%ooca~9c4Cb?-3!Q#w{IJ zmUrGspmo}DvOL|zuc74m*S0lH0=Vts0ge;@U#2R62$tnY3c^fZN+1sj+zs#G>PZn$ zRIOIaZc4^wX4hT3jxBz_0ss0u#@@QF2_^TmZ)|~p-6R~;m-YAdK5sC&<4~;s#9j|Z zFy|*mzu7$xAM#v8oQh#AF=V-tC6$CfEAMn;h~6Kaz(KCe7<=vcqu>f^6`T0 z{q4bQDTjswQ0H@YGNt>KbXGGx1%av!^8w7n!V0dNgfY zuU{U)&ij>OQLI}s(&=EhFHWSXuViJr@riN4GNLz^N9l(>AE-44*HE#wky8`vc;)IR zvwJ592kkUj5DUTUwPBi*Hxif%aWTvCI~n35W~tci7R+HXXiMfJIf}} z@$~xw;|?P4lbp`d3}PH`wYnpp!l&zWvr>|KKh>TwcO@kpNUX%byyQLAItkn9QIwJK za-W6`mxi=i?tvfi;oGR_ql@De(*=%k&6Bn2!I8@9SOxy`Q)zs}#nTon)fH=XC+FDJ z0tY!FEAOx$j9GM^viCz#AR5sZ<*TO3iZBo2un|Mo3$0tmX{sxuG^pX(`IGk26AlT9 zq@%{-Nvu*P`cW6^zQ^N*9)@Gywe%$|CEV~tEBvvEh(Wv+lL&05rvn_-AWpYSHf5kesVz^>ef67-gYj1Na6WD8DtwvSk0{ z%PLA}h+>u7xP5l+Z0x)-+8s1mC1@w5a9e>0$-0U1xcvegSV(2#KOj*%W5^)TT*HGA zA@ZJn-Ca*|(a=m#S8O}(3=3}i3pr)<)Bc^xiNTK|gw`SNqq-kFTgjjH z&r1mDw$nPVu9$t2F_+H=%~$boWFLY^YZ~gq(koa10Q@}P3CL(TbgfcsY;9~OoaHp0 zSG~{e9BYZ?=6#mpy&ta3Dz#Z!^tPv?ch;M`df|0@!dJ)2ZW7u!d>jUYnq7ijUmPBT zvIzwZ^MViTxo&GWU?i%B0!IyTJ2hx5EW5QW{dHp{dvobXu8CtK4uY%YAN$h!YMg4E z#93ri8%pY`%$!I@PD1`4X>T45_4@vSpHoRCoKmTTc15TtAtZ^ig(Az4kbO6X8AAw_ zqO$MVvyI&_V+p5_W$a^_8N0!lnHXbY%*^lYRNv3%(>dq5e%J5!pUd_3c%J*YpL==T zulspEykcXUcVb;kEhNZpPLq=CjuKL~Um<#io)|>FJ2H639OXEczThZ(iY|D zX(Y|e2~#sKx%#u5n{U;n8^m9C)qWJoY(Q={RUynU4?@DDqTA;V{XXu`tY4}kQZCcW zPFxcFHc#dWh8`~M`1}JG|4%Ab3m}%MT2<{0-$Rc#wt#A^AB=OfFLr8Mo!H1whG>OE zbbI+2p&uYTzJ#tTraf|Ik|o;5N;=nq)ySh8?N1I6qpFZ_ zG|%FIfUDTK+nrkN2rab8kkoVh#kz!>k3aUmaK zp6f5J#gjcU-#nnE7BeDz!kJM1SgWiR)LEarg{JgRs92`Gb{FDM}EHmG{&dSmv-YJ#srm|a%J#@p7x<+&FIlv}eT z<_(S7B2|!^q8ySeE|Zo-Rf1@&Hb*!_he3VV6DUiJuIIuTqGAP!i;HM6e7clpUP2mu z4@z%SyjsZx-20lo=z4O}+Jg(r@Iig5#URZRT>1CLytO`y){BPj6)<;VY*6djuNZ9X zp*FE0)akDTOba(&jF@iLArV6G{HPqcL>(6PZhZJ5 zrG~ZBPMLR;uh7F4SGvBD`#M`I{Aza{`%t{kwE`LYjzywQ;g|biHrOlHD;KG0T}N*l zsWj+nA9jhK#8v3Z6i3j^E;EF$-4!`{{d4je{Wb;b759qNQz?GfqILDit9XZ2p|ry) z4|NqL4-;+43iZb0bw(pTzopDuj4cld$tvk5f&<39RWf>vcr4>`<>ruw7(S0_Z5nP$ zB=CX-vrb~R0@JvHF_`cJc8vW-j__$8*Pg={PiVWtqZj%(QeG-hh^F_Y&@fzw4#`*U z>!1a%RNX2R(wo3zhMPdO5Avp_iQp2IH*7+^^n-j-+z73*3)mg?x`Z>%l^7IOQh6IsZq)Du_}~LeZW>gVFL8HM$WV1@Tx71BK13A_{-@2 zXg_V>Y()W=+Z_`CQAH2eY;w)h z=ei8#bJJ&)7h+E(zP;E~g@0)}&f$K5L4P|#@Oh$hb&nY0zS{L;(dC=7Z zfDH`%nvOA2DfYAV4W5ILZl&?6!Nx$%R#=CqIrb7LvC$1B%YXDUA63$BNcofB9rSH% z1nBUuG0Zthzy#*xvRQH^w$3r0trC{>Q~Yo9@@BaQM?=@^h!);*mJ_`M ze#M$0qpzcnHPsO^1LXCoJZR5y) z(~s&&wq7%J|c|9Oc(vg%OH;K0Hims@l1R za)BJSF->4lRGQ&Ozg>fko|9i}Q%l7TvDVPXTu~a3aNiS!4Gk;N@%WJxo^8!{UUbS@ zL6dzz^|oVMVGwT7w4}q98rpSgbPxJYgij`8o>%6yc!iW`wd;N^EGq4Ohj58M)?D9` zLLId*ChyHlht>MCKiy?G1ZlujmL^cDEZR&40Rp3iD}`}n@DGq=_eSmHOIWk31WmwL z`A~m1Jw!%v{F`$% zLkjYRl))8%gsVW?;ABtNtGknpXa5FAHUX;MB&GeMU=sBBk2d;*>F$tbCkRR_i`d$5IYq)`5vD0O@0h0JaApifH;*KM7rRdDJ*`-&b@fHoZ46<*`b(3 zi51|*U{cQwu3?<$4I50+Ss+QOADIp94th;KMT@wwpa$ff7RGqW>ibqe)7M*ubgY1&NFo+c@9|J#`gt{x#oR)0STliV zEnv8b-o#Co#&n5L5w{)F@R|*>^?S3u&P7g)j(g(94?@bB0%vOi$e|16Ojoo*A)i+X zkpgm!KWi-KK8Gb7TT}jazvQ705agE!STCSGyJFWRYv?E6TI)632nOhFe+m2n*Qx&m zTHk*<4S@N&Z7mT$p^=zvIVWn2R04fnl5g`Cr_%Gz*9e5=# zLMznsY3?DRbsxa6Ru{>cDIUy=+MbDAGjv;#-Jt{Jm71BCNzGQIv)b-1f@gte#$u_z zc`biD2}I6{I9zpMr>|oJv+`qBy(l0WnNdOM%6gKr-{TD#uDGbn8P|eISPS32XKiTT zPsgtN*R3upcEZw@G!rJEE8kQ-S9Z4og6_9ntI>z&tT~7&&^XJjQ2CjJiu%VRP4o($ zLs}Jake7|2K6Me+eanQCHERXIKZq*-G2;7yN|e~lr1}r+{@mT5zWwHfR`+~UBYQ?o zAb=UU8(SUAA2(a&Gemb3M75?oME1C5uow-(G64nsWF+EL=fwbTmHY>^9d<_eIpt)t zyM*CQ@da-U3-?0Fb9(aA1}5uW5G$i2GEsmML)=})^B`o@_{?)os-WRjX1kOk)rP{Q z+>ie4V_?)YU_B5meA`%{S7T$EV1Ev4?XoK6bP3OLyP=&HI&xi5{!cSEK&qDksg6vH zO6pkw9<-s{w&RPjX#EW0_E{ajyOjBXei%ixjL{-$9=N&YWl;8*%kFKwCFpFtoa?mZ z^*&lAAA-!tIJDZD9^hjtrg6R!UbkVUfhG+yi%F~waF1W43i)xb#jYt{YW*b*pWy&S zqAim^RyYBeiiM~sp>ugD57jhMPt43*a>;NE*b= z1Om-$_3AD-pA|kA8oUL_u=o`5i6CkOYb98$?zo(@mMf=v>0Vh)h?Am!zvl=jgV`bP z-vjq#*H+cF$6Me{E#NFks$bok2X;MA^^miCaPG)=;X9)|(sO5fUBM)NyJ?r>KpdG^ z3XXPkFCNpp4@CUTZ>d}cWz9pVJtJR$_ShU;*e`WaVwQWC8RO@+@W>n!y0g-ml{TZPR)xhm{ZBzv95 z;vLD9Fsp>r*aV@Ou!!e^=5A%`CVs3>M=}Lp*R=4;LEhQTDN&VmI$#kKqtigM%Kw&^ zf}R1!c(H?EreW@mIhdsDm4wE!F8pO`t)O*vAA5+Dqf>a6 zPzfP0d(1EP;9!VTFK!ya@sz%YW(X(aZUAZPiLc8rul4L(?@rEMj*U-<*cT9M*j+b( zE}`NfRdJ{IR30io$fSV^cDa8wJ}JNp})S?u_=em-S)mHhJlNCIhe8$ElJxAXcqtl#^zep^8a7AO%4tny;@ z-5yrV39fs8GR|b|A~<32Kx(uWRclQDfi3;n&{*plxR6XujM#CgW2*+hDbxo^)z?G{ z?sXLYE2Df$a_NyX*xKXIJcz6~V2@Xa`&}E5!;g8~N@Ts+Uk1!Vi;fPd&x z{q3A;CsD&sVX+Hq7NjTrxjN;X09Z!=b7=w5>{n#i=+Mb^_>#1#9)M$mmw{eXtf>~k zzK1<80X=c6@%Jp7k3D66GPgH@XIX|W299)8)b&`(F#%L&<`hk2ymG;E1XiMy0xgZ` zCwMQgYZgZFB0z|zFzzsSGiCQ&92an9hQ}f1dVH0^bPP}ZSH&+dEn`i0a_I{9uV?)x zU`lz;Jl^@!ONj%)?`m5v5N{g@1L)Tgxq_1WgcH=XvKMy?REZN-)7cQtzMu-7#EZ9d zue0YrVwYZ&)UCyt`{+2q-pL>`Ff$$@Hg*D!4_>vO@FuA1%%BHVd@I6uHMTY}8-)Pk z7Fm!V^2Kh|kgK#-QPLI)W7}TncF0#=ziqL*=!9QmTIyp{pq#ia8edOOldUO_6}XWO z|D3<_^(p0&;I3~(Eb-_gi=I-yc|<0Ugx5oiZxeXHQ_x7+1&nKC;4d}hkK2A(wVOHa zwcMnyA+lI^;D9-a2c$SIu2bSLADYFc4`ae#9hO!SJ@!QJrDB?+yD%iQL9jADv@i*O zh?cg}^=`Gl6bRTJqW6j_mPa|;L7|$m^JRX4)QrpitAB2*IC#jMv<+G9y106BQfl*a zIb`;ogegkz%pfBU0aUT9|6f?tt+FRdeV@5g`=P!4E+)G^P-WB&K9a`pMi+Tc4=4B{zHk}p-UiX3!4OOAbzx6)=ll%2v zIy50VP{pNg&iy6<-#lKUQ8z`mc0w=Ey&iMoN^0B})2HWVrHcGf>=u5dd!HlcPi+y;82LGo)QggH%55Pc0kRbJV<<$%+_v9&lRM%uq)}63M#vD;!0VD~xK#Zt zP109ZFJ!!Djk-x?M*^s+I5Pwl# zuJgZ9!tXoE)d4LwbA0Lfg9;V0#oS2uRJSu(9~(Mjpay%eVQ7&ykaerN>u2fxSE^2s zH&8dr(|Sk$Nnq*Q8pBwzF0A&&!kJHmMN>1gj=X-`Pb;=}#wz|ssQIx8Z|(pAOX7W@ zm>(It!Pz$wO;tw8VOZcU&bNa?;s^Ym76*X`_Xye0(O$cXwMJlJ_@CUo|5?>>W567G zRXrJz{{HLo8^-~T&V`BG=fCdv&k279qI^5&!Fw0K|AFTg512tpQ2Kj;`}+sY0PYs9 z|M(}h@!WpE-k!9w&H15X| zzYYDJh4&QT)sJ#F8~&Kixh?rjRXox9m(OpU1t?^8p5%L1e{2^(u~TT3NWlLaPx>aH zKH<+E|M>h5%|>AU8#*R`%XohLzc;S|O&@va6!V=r5_D{9``*5^k^3L1|3@i;o&(?S z``zRxt-=7<4qbfYnSWCU^DSm;&Zv#$4|TW>( z?*c$eNpebuf2Z)hf3~%~HQR=-|C&Mml;;URK1U4p9oYVt%!2yP-P;Hzaqe1Qslfd6 zl>WH$UOPLd!qzI7XF<*u1TSJ>nPncL{IlY0i!JJ({Sk#vq%V{Q#5NNGS9NUr%m_r&vw( zNqZSnpNJKM27j*eX0BT2&3qzoA1({M1ijmPPW=5LH`y~Fk6YBRJ!8_RNz+)XO zBC0OFu5(mqx9RDuynW_&-t>FdejEq0w8R^H6F9)W*xIbOPhn)y?kDf`7v<9od0Jh^?x?KunR473 zs;@M3%7m*4ynLLGj^2A)DZl;Kmk6I2a)(YK&%)KJNT!_KQ}4e zFxurjTB$$8Y508J$@xjDl4QwlM8(v-tpDS3-x%77HL@>uK_Kx6M^~di{*Bdba_TD+ zHJDrErwR_(Vw_cPTM)lqbb$8#|Go(U4vqZ7{H1?sHj3HmU6J((CfNsEc>UKRZy52| zSh|y`XRlM`B@q=H7qfrRzyDVfyJnu<0CH5c!DmPWX~`a}V>~JEakyOI`$pL0-7wmT zZE$UVkT#*97^QOe``Eu6nHy5OhnuP>#?@d!U7((7>|%vDz1WS2E|&YR z_KKim``Z9|q!*79RRl|+akSm*&G-iQpnrDy{Gz2J=e{yWsoECgYD6_KDIo}Sj&g(n zaOY4NHs3#;+47Oy#+MZTrI%}~bx4>u=@9n61p%1p|B&{7*|pjMW$!;w857FRR)W^Zcnw&Ct!5^V5eu33-Uw;HIU}Mjl^QXYE8|HAet9 z@htTR#re$SD^*$CHf zRO54l_b%ae`BW-RJHN1ja-jp(?`d82Dl1{(`aeh6e|17c8kPLIqDUh+0KB}_4^imY z7d{(0RM=>Fs<45j>Ua$PeBSA(#kjATo(X&MrM=MOA3FbQ9MiVdW@rE4)~~y6KM3jh z9oF$}A3i1a)nOUXXKmlRS6Pg7fLVj+_9ox--!P2GHs)w#xt!Y`=Q=QPY-DUHp3gAN zU$*-L4&MRYsD(z>CtE9WT z>}IMFPX2Gr16|;G&N5F_q?IBjAHrRS?)9ea0j{?>EtU=k-A3K}uyknHEgf&$cZwme zxLj3h+hU6)#9mUoZ`!Sfeo%a}t80Dsqa#e~(;l-9IiNPHv~hjP|Lm>&S9r*nNv_^J zxhRsmcrW`rIVBVQDt-Yji#~(oqGhe_(yq(*IJ0)lNbPNS@ zGuaplzoU8~Nl9D${_tHk3lg2;sk;o%i!h~R`;;HH6B(uI%CCJ;OOdRWQ-T2Zn2mj^ zp-<@G2^-=7iLo5GZA{2^zU}-9(yx-60fr?hveVZCmyIn;e4xjf&_PFt=WUg4nCBbk zMtrsI5htwEd7-PDiM_Kq?z@h^sogSji($W-x!wY5QU@k}NK(p+tp;P~Air$T*Ewb6 zp6dL85*TW|jNO{DOEW!D;fBws2aMbK+o}H9x4SF$ObCPj47&W$I~fsFucV#EE?O1Z zOi;?d^_hXQHcq_S5MMz~4>S=2AFVw9)^k`0-}3C@LG2H;5uyaS{nOQe*5cPWO`&$z z#*&pFW!_XJ@xE$EQC;fNLq8a?CW@hk>7Z$7)q0veXD5KC5_L|wM1{&29`E$H7jp*d z)%!dsHATnYPRbxZ&=#GEGdCc0|4F-Np!mn*xk4#LJ=5_X%`Zu58cP{>IuI+9t(L>l zdtM3p=wAQ!5pIL~is<%)THUYjaFr;H?2|o#t>2>YgG$Xcw8v@B?4$zH(m5C?*~-vJty&pGF$ z)<`9-PZBMMk^)KFJf-nI!Jk$hgDqJR=+D5cF0Uk?L(ElgH)h(Vxxac4csDlWlLngmxX>g$TLt>13KP z{r((dIKXY6xnPcW6POgW(ytw1Uk?i7gLdrY6`+TYk14JK4IUfliJQrJ!}LSc6ixS3 zjPcPrK*aY68Sgq%mD|)zXf#v##z{#6ZAxUfp_n=-_dhbuKKdIv$7ek26a3QSf?1!A z_gC1Y<4Mp(Ejv?pI5V!01VBZ-n~5ha zyqV`#4JNsRql>*lmH1wIa+bb6pFo|<;Q^j)W+B_pi;2wEeFfeo`4Wo-^TG9X6TD6~ zfgAh~uu!G=SAO3_32ap^#J&7-+HaZfUPa-jPx7F-@u;%1w*yTqH%>Z4z_)6W-2}9IQMun}ZJASr%&VYe4 zOnp3e(;eQotno!nX-t8s=1x}GxQn5cwyNsC1hV2B*D0{d`a{uPra0-3Tqk#!i&ggv zfHV2HmJ-`pd&_L2M21nr?y78-Xg{$fj&-0_%=8*^$P%bL+jO5-*Y7BiIy}9}`C64j z)k0L)G<+*qTPd#9F{)EF(?tJwZyAG+jdC-<=)T-cVbgI_h0B__Z%jG%p&i6du@AoCIr znMWPPjW`6ozgPQ*xm@kyHU~e@_B_IFgbd}YboI6^WhlP^G_2+)`{E;>@JGKqi1c}) z(2i@da1+|+v&&2ObWJz56I`NwEq=x+T!_a|Y;q`O!+Zxee=PR%87)J1&^uy`xFY$u zo}OQu{GWHd+>3u(G=SdB_Ynkm8?!+ZW#K-m>zJLBCr0 zg?wce|7yF~{dfU3+@cz>p2`jE;oqJdwiSG!?NM{B=s_RlPhinXPosZ-{mTZOE4GphgTX`T4=lTKwyLomD5sw>d~#vel8=*&hde*BVeRDN63@k(f>0>ovEOtTf8DHLea!$GS=?CY?s6WYkMExi zY&|B9rj$qbJ2kyC9)GLR#c%Q=>>PatVGKM3vNTm;(-PL0ZH>&(6_#ztMD*_u2qz=2 zFlJe5x0Mvs=A``xL4W#b+kyPQE(@+?*NWCO=#AorH370(o}nN{3feU`$70}YB9n_# zDS}kk*L`j%rM}J4jk%l8>v-%p_NE*!>?|XS8c0GC7qQ_#QdurT_t>SlrXqXlWu$q z8Ry*3O=4h0Npiy#iqlpYHFXnXu001Yo=AjCm{k|$hYO@uc{`441GjG3UEo9`lr%mU z`4NJR>uD*ho0o}yzMn9aPU`b2QC{@;x^6t)R+MM?MqOI6LeB7!dA$5`&8bvcSwLi9 z)rz9Y+M32?*QurKZ4gkI2uBcGDA}4Y{SAo6+N;Oz$G9~xRW1BjO2mqm#^upGmfm7A zEsbybtdey#Gb8X&zGHW|@aR>KT#{W+sYWjFD(uu?)iiG=u~$E;I|k@}()KAd{j}aW z#4yVAf|nEDX_3;;AZ(l-2$fLdfI_kOB?9Slr(06 zXu#7YBu2!b+W80(cafAR$di)%E?*#TzZ~Zd9U%ARk-S(?+Q{VEv4JvD&qtb(3k{@- z=~tMCaB*r)Fd4;3a^BsOisYJldxOU(@-czqj`BUA{2KQqScAhqANc?HN&(^CtVCMrL`ne}=$^B?k8 zrc(Bme>!A#jkqb$_35svT0yJdfG67a&RWWpR35RY&g)}PdA$74)dVV0GoRkVFFo)SH*;%j0YjE zGtT(hh;cV$w~`OQXFU$v$}a~G{zHEGvg+${uoEM|<;5Nu_u=W4VF9o0cDQ7&sFcFol@3kug}&w17vmH z)YHgn)X7qjE57xpPfAnHu99`q}aa|Ng# zYxC{G!kgep_PK9SyTN)&d9`LS39ZehIynXu8$x=!2)C&(FE0#?Hdz&Ut{M{d$7pIU ztT$WlOoPl8HQl;QyoE-r za@*Hm)Kr^zuNs&zgyHAB4=P>Tzn|(~?}Mj$nuW77+-Mns_|=7g2#6HjjS4*96$cas zTc4>n`bp!lIk71ZNWR2%YrRg7o4cr=x=yJoaoa@4!w^`;M0P%HraBq17mUzbGr#QW zYfQ|%V9T?(tS&9Sgs<1Z(O#uly4Dw3c5=BvOBRz&&Cl9)Nm>Q(WO|(#QSiXVe_@)( z>oAC@>TQGfG{Xy%#>wX2rmvT=Pdk1BB@9MLH!jriU-9<{zds(bb_uKzAGGoz9~LN_ zYLOQT$w)yfF)zh33gLv^%p?0|XJ_aJd$4}zW|!+gOecnl)?)mHi>+|bFd{OT6VX?y&|A`+Xf>qnUq$0u}#tW6um z(tN&@5o9^ffhHZrHEH(-O@PzD7!(szKBs8Z&5T?h{4(h27tKfD$QhO)InsT#%ofiq zdCDp}GqC4rBlZ36%Y)|cy8ujW1h^+lxH#=sAS4w^cR2i^_=y0oXrfUomf-7_X;MB{ z1yCzQY1XCeYk9>qoN8cQ-;g%t>%`OLAghuF<+({%O5p_9oTJ9ha8L)BY=MfBnX-F( zttNTgG{X^UDITo;X-b?f$r%z{ZO~IVP!$_hZ3J#v%9jyLDD|yVOs1W>0dx@s-sl~= zpfIltaFOL!rjsmfr27q;`2yS=$0X*&;h-mB>-$<*lX~%3(PnIsiNpi#jsq-xGj4pN zjI{lETH3LtPAATALCHum`N%Z zzB4_8s4HCDDr?yx97Nke+Eqt7xD{x6p=l5nO7X)l-u|v$O(Ar30~M zl=m9s^^+s;ey`VQW{|7J{Z&;}ir+9!c9z3;130Iu52A(`!FeKlaNWx7ry+_PA2NoI z3fNF>Hu2UPdE;Qr`-C_wv>{D?K{YS*JsOvnd!|23h2vv-7fp16m!!h=N!tXw=P$4I z%)hHhC7i`MK;ORGxd`1`WA(lvSF*qip9YzXQP#Us(|xw&K8D_&rUh`3*tAz7(a@B6 zj}OJ?fzW*w_N@BAKt>>D%v$PE|BYMmz1A(t#thNRy$d>LMihEovNx2aUt;LznyLbx zL|#QId0^t9OHL&M(8+sMEQWW3;0M1{tx;dWCys1t)rM@=|R#U-E4GXSw(V`wD~nC%hYZvKceJ{ zQcop{ZNRYYrA-0d1EmVwTHu9zHzBoM9yc-u*S9j;}84X~BfLt2k;(4NF4OlwYVcxLh#!y~RL#L2FC zP6gUje3Zli<5W56?*&n#Y6Wv^3Y*gu$h}nH_5)LFj$mtk2f^~z_ zFb7-8i%q1y0p9AY=%(zI5tU;!Ulq)+4GGZO>`h6Rb+6{6XK*Yl;``SD4(LX$GT2<2 zIE-Ci&8!T=;wJq$J;eOcF^*s_y(T1$7{GL?UwX_=pP(QqGE!{A#^a}V2Ar21ZL38TZ z*Oq*kA3z2wSH(*n1hb#^6L1>1hMSgXnE}x_gjN-fTSN%U`PrW`digEa%X=S8QAqXYfguLveQ|==tNF#MYku#? zA0}R6`VnniLg{TiQk0^(;OE0b1>C}=NW+~B?sc0GC&;IxHVap)&<^#QchKsCqMKDq zJWqJ(rr1RfPga*w-66$|mbn&|qA%#tVy%|+Lk}~dvkLqNA)s33qVjFL94HEI(f?b! z5(GV_xoR&hL`|{h)DmlWwcC=eN2xx7zN%{NtzLX z--aE7iZo^{d|DYoi?;eRp09{hMxDpM3DfsVGn>L~+>&nVzhQ(1MapGw1klqV-mDkO zrwmyB5yq7JQFmAkMXkwv8~6u}V~(|W)kZ7|>DS``GyxF%)b;ooQak#nz)EVz*Ojwr z8!CBpC0EEnk=bie2CkB3btgLIKPlbDz41fU#zq9>fw_5?T9YDN@exk+U`2BN`_iU{ zUB{P{5a3iTi$G(_(&aYhg+Y#x(m+KqQl1?9nY9`5m{NT4(Vr~70_hx?8WEMuYwA0TIK9x5LB+uUALfRXXV&olT)cY zDBKP&GjXQTMODBgrfFZKiq+pR<*xtZ+f*UttCVZRV=AZd`Oc}XF2-G?mJja)eTs+G zSj<;JY_M@9#{?q4XSc|nm$z=$F|GO=Tti><3UT6oF82|fIMbCeO=g&5`Eof=78OG{YRC)>~keK zk1CQ;+&=phKj^`T?5C*mj$|Ki3OHZIT}gQ^z>c+%x6I49#u!;Ud;0Kh+IC0`tBP@l z8k+Zedf@y3BGHSZoNBQjXh)`_;?&IUEkxa>Oqi0qVCnDj#ULQI1}92=^iv(d?YjNB zc9eUdtoE52-6~vuoyuUBvf!WSr>fF3Js&m0eN{FV`N1;U9S>Q{-}(0f(yt zdxmIK-nnpZ&U;W3-vTU8Mm}}|N@qCDWJ;_A9Wh&U)8=x%+T#{?o6hs=QUOPYMTyl9{K zm3&pi+lKa?PH%lq7m z2~oUiC*q<>Tl5mxD=jb3P}bTKj~($Mxj!ZLXu0?S2-+qApw?RMP`{zD|Ae zJ5^8R5liVk{p1kyTNGqjy@2Y4f5SSGfcVRQhMlLd-jaWl-1!N*pRQytY6w*|!E3qE zO+C$$?_5jz!W6Z$zA)f)6b)CufNw z9;P81KwJ|=`!K6(b9gT0vXu=akHHJVBvTVE4w}DO6qUU(nDznZZU*b`vAD9IH-86~ zotA~8V}KfpV2X+(J6*<`#_=+^0>KJVdx2w7k|$FW(1T0MpKEWumdr~7Njke13#y}) zO8!KHVhLz-4u5ut1+EY|>0##1&4oSHNq)ZyE8`p8zp-DybPQSc;;|dYaz@LGpCpk4 zUimWR+_Sj=8!z@O*o_1C@SH7icpLb8p3GYn0Lgp1h2#~4{)*(~8v~ajkgIojo6P_n zI%|WX{B)5K%6sECPFTXIqdehjAMP#TAmC)I?U4uI@Da}vX1#AvFJ+F6(MuAgc=t%m zV`M<4J9$UNwa}aW8JLgD-AbR6EZ{-4f+pMTOz*{i?&k4eKU)t{9As5(2lKk>%1Mgi zMQxU*PNUp%lq?gLFRJWXgyKZgSSp^QW2NZr5UR=193ajd+ln(sq0{F5@08o_#Gn7J z?gz3*eh+3%dlk&;&a(1#ts=Lhc|4H;U=z*+`&uDGeTaFA^oVoZ!E)e{v{N|6DqwGw z+HNH&p{orNxvg9<{OG8e865cHkuxT18XDLchn-CoYPhI!%h3e<93s+(eThbRC$Ieh z9`^>V`4xW%A-#7ssMs}q+Cu=X^;38S9R*aH^ zN)`mqNjfJ9-&0ZWsNO4lWzgi$Gz#^~N06OWYnh9%^y#W7^3>X0x;d2+nBeuITS&S_ zMCqHW{8Xu9_u>@S4NCPpzseB8?KwiOtCkB$<1g1>aX+23#y$;;BK=-Js(_5>g=1-z z4SB1N&E@CU=ag#bh})M(tt#rPA6aWIwWkchnv7lv#^`@P01P za)aVn;teGWBX_jsj$Tx-!K_?+#@H=6w~ZtuVcVm`_k^VB!6W-x&Un zidSPqElW%#o%I;_;_9<^VdJM&YUd(uquAO;VlJPV^Z_-N!b*?3N-! zq6gXmHj$P*^Qu3}>rj6j<7jiJ@RF!@@XhdZOef^E0T>ljzD`JGm)im?`PJ#iG0+t% zl6k!|N?pP24mOmTWmVn;j`1V~h|$;s>)>4l=sM#S4l2l-9rLF()S^df`pTPuiJROI z)1G&sT)_QdOZQG%)DWd)AR9M`?Bb2<`VtbbRG;|f zExtvmaWtqBjR{o>b5K88$yeWV8_nn9X$vZG@>Il?ZZChwhlu#n!Fz-i6i1Go_kTNo z1ttu@N{XH6Vl@?$l~MHC<2$dN!E~)-%gTt-=vK%53~}H{o6k!%rUw zM0DGCV%e?{U`zVR(s)a_#_dRW`N#UetOo9uV}{j`;pJvR$=Z5dDdzcomGS1d!PJsc z>ps_v{PDakxEbip*I;@P8s@tQQSU_raIpZGpdSg?OrMT}s}G0e&n!Xqf{-kh$AX#Y z%cEzG_8l?jE|z%dm+47z0ulgWJv(T$8B!pnB9cbeo z?kj1H$I?9#*DvLR(S+`7-=Md6;mq#CPwLgQYby&q(du7z(N2-)J!Dc{Le~w4`EmMd zjI|--J6T%YJ};LkE0`46*PXHFlY>d;=m#pP3X)s8WBCudQ;OkQM(shO(Y&;P{y2ZA zr5~E77l10yRB2-yRn_!&VqM)c$CRk=l69nc8@Udin0w5vXcGky5r2VN5xlSG`zDP}ZJ@T&TlfgbSk@v8EW6tsBzx>v7Zh*}+a| zNLYlFn?zyY!|9TDHJB=%ncKYGZ+W^4&oh_mlLgJc32I)giCj^ZF8Is8Lg!sXT~Q4g zK-)Vti}ALx-a4>Eku!BIchhzkG;N7_B@xIJFv$0wqy6uc&Wa_E4KF!KRrjc(-whe{ ztE$yajKVpKmtQE8f&rOq;l$kJEXCqds@SFROPxx()b> z=id8~FJ7^TAiW2xU*Z=JSLy;hX+NMwW8;FWBK;ejf?H|{Ux$?S7FiUt%35iPIN9t* zt>9@=r;odfbP?#K(;o<>^Ibb%;EgUT^-OIH+owj6B@p{Y#hlN*;R?~|zAN$ziuS>QaWxpSsB^IC6ZMgC075(2g=pv*@yV1TRh+Mh zG|*cXKLIEP%K5((W9*3)A)T@r%DPxY8m#*!5ac+2HSIStQo3)ZA$L~ITe7XTD?k|y z-o!zkl2xyQ$DfMpF_pmeep%4kCgVGR`h4ptdLU;4R1{q4r~L<>PIw4xEGUKIRT-1g zUfsPgmJv{d$MV9r4BubILxUGF_`x8@B|E=XsTrsnor6*;xa;|yQ+Fb-F$AR}t-lbe z1FsJZ z;G>*uVtsLa*EdWKfoDh)&qlxt8lJvT*-UE=qhkr7tgyByZ*eWa;UZ= z&IVM6dh|*l#@;C^8q`?G1K7${zKCkgkvpmwJw!8l{wFT4PU?PlU=X z>6|_W3A~|mAnc$mkm9)bL87cK2Tm3S%_^-wTkPWm)zRYcRnB#^H;B`N))z{Dd-B){ zJZmZSAJZb4WN{J;iiW60iFiSm)P-;FU786Uf>O0u%Vuui$?88LZo-*kF9gl?WN0K7 zl%@7p2t<`R?L2X)67j*}jIjBbq}+6B#OB;lXGeSejOL+kt&Meg=%#j}lPg!IY8>Ah z^H0(09r6l1CV>X800b-yF1m0v%fEpuR({ahKuwjG8%SUzl6}X8(-IXv@{sm}t3(0d_^P2&cUGJk zQrK-5jWu(S2G{BWYp$j)5cnjsyGO(nX`e`h5kC)FI?&7wmdQ^Vo85a9(g;~_TG)jc zgs?kL7&wf*?4BD!F!2iy#O%k`Dlblw%QS>f@-r}KGRj^CmiZ`> zZnrENj21R`a#OMGhb4NfLaR+D3p1da@ISx zDxIDL6EXSdAI0t!%>Ip9_}~x z_}qt~IDd)2G$itR9rLcOs1+z!+#HQ{o))6?_TCG@vr1TMS@rH+(UEc7)DHA->S*tx zKwCxT?6&FlXZzQbOvjlMG7ba<&KlR+OKuh4@MDlAtL|*M@k=V6ggC}2yG+_W4{R*R zLO2S@M){a^aSlyYa1{%eGD|e!bp{5Da$~ESnMkI|eX0F$3&SK@;7kKJ)@q&3Kw5cK z&cYS-P#Kd50jQEWR@1?Z$?J=4AvVt(x{B3$JeJFyyyDo(eO20`uc8 zvF!eZdKCUbm2PoHR{0d|fu}94yW2NYB`?*pXmx?`Iq?jErJuDEo8N$mSoDHVeGoC| z!lBK=#nMerD=}3TUyTJ)>d2lFF_{o=4->d}9E!n7&V6d33D^-ZO+wo%p)$~H>T zVhJI8S+noE;fqRTE&DEtY(p5!FeKTxWEqT^v2TO1jj_9D!uR|8mR|SXfA0O`-amSU z&-R?>JllEBbKZ{_oM2;7o;`3N&(#V?KN4u~t+QEWkj4zGr4q z?(y;`pHEUfxr+~_K)JkJ47v%BJkJiSnIg33H@mX4;!Z`WDi}7ZEIV?JtvDz|&|1WZ zFmxnJmW-?pWLO<}G<3khw0=6}5^+Xmu0&e)#AzbIr3CD1Iv9(u?8kjXIbJ@tFcB0F zvZq{}Wsfh-pDLYb%zy+x`!cQbf~7tEj^?TSYY^5lvuXGu*iUjZ)V}5x*%pu~|B93> z7rG98+Dt{z$+$WE!|jQgpN&(Fr+?=YC8?>nj7Q;oBQhm-czGO*{Z_B{QgF${usGUB zT6(N?&WJpFb=>HKhQiI^?@vzPdL@Gh1^#z{U<>Tue_b0+xGF(Id_PnRuU9T&WO|Ixn7wzrSpzWtgt<_JZA?RVDOG&lW| zCjAmGzd1tnO1k~;+X2zvUX0y1sHkTh`idf%s9Qsdml=b8jdN8^MrZ2Fo3JcJAYzL4;P#B{B?=8%p}lA7QUj*0fhlk9=-3y_yf zWlZ9vZyvC-NopVbBvz*9-1Phzs9`adqZSR?VOCuhanSqNOPfCoolf#TKwWD_iH5sb zueiU$?dSV;FQcSv;gyPqK&XO|=YYnHOqnIlwvl&ZbVM&fO#yzmne7TeXo^G3$kJ5e z@ym)DGmfG_g8k>F^{0q@Nx|C+)U6k!;?D8lj;#irPNGa2op!v7BbA$;SvI~Eo1ee= z)KgO@J<(OdvIR0nH>*)##3`(@)8>s9uB-gw7G@Xup}P4e$Od-uhiI;K^aEm1)XeHzk- zm}us{mCC7PhO=uf&lkIXf--Bse2pqz|1|Z)T&KKEU*8+g71db+ZvE-ug76dQRGxuF z+4sbe7`Yejqq-g!U29zI9N&1q9>6K!|!>tMPnW?8rQaU?q zf`wkLxkmZywa#Zn4%w;ELp0r$Ia&t&%H6B{8hoxPb|Og-gj3v?28fH&U;VeKTbagX zl+>LR&S8vVER}8_@iKjeDD!1OD7a`BnBK5ipQcJ2{Gwp5&q%2^e-3gwj-@tmrQ3w{ zQQc_9G>g;1xjS0EA}?n|Nf#2hndd+Go&@DMP@6tHPNg@0ZsnB&t=K498S7diC$2Kw zXoY9ws!vS_LcBsjrpJ6jrsNG3^+ey$?Jt|n#_`6_4b_}15!+OF1L>JK=fcSIG!ZpxKI$(VD^XoyZe(z^lCK?Ax~pu!n8k!D|~?1 zNG=NiAezj_Wu;yHQRwj1ztdw{B8?vRsWoY0n%P2Mxy;dBPLe2_>mZ!aRiM<3Nag=V z{4)40iX%Uegn23x{}B0z&lGZkuE|iFcsLAWl4tojQtWEHg|)VE!s9~Nob3?68E8q{ zoC(LCty%nQ6_5AGy$&4_W7SS3byo=#cg^Fvshj zWezPG_1nyM9>kOIaj_@bMk^d98(=HFXR1Iu$b=g<_x$r9?)OAamzO$M$Sd&re4CiR zJi=7K6jY)X1BGVVVW9VzmB9D81{+D`@XVGX7OCg0t;o%|G(&g$Q+ ztU?ZY`UDl-jr#JFC>~kDRt)H#W?p`AN;d34D9zG;oOol*8JK?1DYP|=(&qk z#;Jt243;?_R*R1}@_t-$pL;*yIn6Etv>W@H5I3BV{1LRsef9` zGJ(?VHL4&Z`V_}jf_a-S`sp7((LuAiWZk~MAGa9kM?7fw`k?O6dCOUpE`j5 zz6DyAqcoj3VtI_u&avMbQDb&DC$O5!zVj8AGP)vDrM}!c@DU+zgjJdB6KhoxyG1%3 z4YOioed9CcL&=tbN~hIRMU6Eal|ITm=~-nKI4=50nfpR{Q+7>sP|&K4^+aN25U&jC z#!Rf0f{QiNk!Q*ZhI?}%4z#BXL$W&%MOO|yv$5uVS;{kDliyn^;_yHnJ{e@Qe5~S2 zlgY+}LC{*Coh-_L!Stu1f4ezrry!d0s?kiQyV%6}lZqwWBoW!EwGmD`*c#ma6LXU*9f1-DyP@49s6 zhpS(oJlbWOH@6J#|0}suIDT`SMmWW1Qu%#b&XjZeT8DA1yDJ=nLSZ8OfkU{R6_l{* zgj6N;i(J1Q2VK%n;A}4Go8y1HJZG3nLhK1Np~NqAwm*<{u2DD4%Xj3o+6& zz3ZCI+(YvRe2)?o{(4sJU^+iz+f`bLP_$fO&dige;gk}DjjzI|S3kMN2_;x{ z@Ta3c&8MIZVku4`X&V=iLK-J@6DEWZHv^x}lvFk>uH?#L zshQX?wFqOH(cz*xZe0(Z5{|RiRt-y}n_WGssp=miXV&jrt`+C1kuFbB8*zr+t@@r4 zUp@^iCVF)EOerL$&(#|HG-%N2dx?Tbxs$r@)3(NImhEL6gi~S~k}T^fY9x7DhJw}i*7b}Z^hS#rc1aT3(d#amu^+leNty_mSz^0-E%9Q9KDw9MaM zdj0}F)9$2J;kLTEId6}OPP#u?w$Ax@$DdKGNL{|`Jw&%? zX-H>}tE#ECb%i9}J5OS!Qzs*q?nlL|7SCB-(=6@$A(t97EjRPhm!?$>zN(7nRw<`G z62xXp%l^KuJI0sRFoSW#zJFy|HUD({hhY`!$C)MexW!o=($`sc#)q%+`t_HlvI^-- zQYGE3yDmEDk~Z1{wC$rrovp9Xs(P;c$aoX<(acO{`JnGJ!{zD|ufFl8pGzs>exYg{ z>t^GUQ+Sr8&}L?^ap)AcX2v<%V3{w_#*$_YL!@y@*|6CM>zUAY8rOW*OpV1mB-e7; ztc1-C=qSCPvM^pv%l5v@LjeL-GzD@Aem|4eQQ8mBVVzwsGPy|8S}zKBuRUrj?d;V4 zId##SAVL{GKV@>YC=g|#tl(1o)7fTEXQv@vO%0~RSY9cd2M4Y`ce%C1HAH;!_L?9L z2z|}YQ}EE7t!?Kv6MW?0Vn1QThqx&|8z&RGDBvItLE8#LJv+N;(;Z%gr^zrb&gH6k zyO+F%-5Y%F`>aZ2(sobli}4H+I+=c?#*JO0!+otymrIlX4t(a1|{J(W9ZmB(;rxixb=s!VrmW@oLyhy*zl+P zYL6~;VyqtV?)Y5lgQ!{8*<~i}Gc|DS45+?;&`Js4RG$21UzOqIzXqjgZ20fRj^$xO zRu#UyYIz@|wG=dLY}kREXXn{a>dxiS)iJMZRdK)`=hjH^n60<^*pg%awffbRb1$;< zTUl9inZYA#ZS|Vf@fYI;;WVA)o`UIO=cY$C*UJa2untDL5i^>QmA|U$*vnkPazyvN zS?cx2$4LuM^Oa5n57yIBr@!b-3o-2}ikn&$8+rXOzYaK5(Y9j^?p6r4B|GK9)((&B z*dw#r@pyy~)`d$5UgT8sTrU|;nxH^scexb~WFPf3ya8P^ zx;{4+qIyM|ZeHb7uI+7Xy-N@j+ivz$OD+MT8_-~-k1irQIfbI40|^fF?sIQcJO+o_ zf)Fg?D$ub7eK+>!Rr#~f*^q=~+rP4K^XCfQFUir1Q$H#(Y+N8C9c4x{9;eI^ZO9RkJfJt`p;6~k{UW=td`}b&RlpB$P+U7rMAUt zO~u9f!(!tFHh&)q1=p|1%4T)A2ujWKuRaNUtAM3imdJ_pxuL&*v9LZfE7BS^_@f<; zb%2yTeRes%rOh5%DU1*=7eY$My+<$`FuHs6Cv2+3sn?q%xua85^II+Ef|Lr~^Tzk( zEczMH31y|4{}n(eNcU^Me}%SBMJk{A%t z2&S8x*ttb`CwJ^jkkutd` zloHhUar!D+#AHq)D7SIZ4k^E^4`E#Gu<|7Is3YkIE`FuD8NsElx^@}b^2sh&Qzc$- zmN_$AWwc;w72a${E#EdY4_InZP}da!xfJ(=4k`Jq&M%MYygA0Bq$UD~PZIvXCKWPO zYEkSPW>>Hp8tGAD4;{(TT=UbV(KMK{`79A6hg&Y}a&OC(`BW+s-g6|X>bYE_@#iI% zm}?ROgLbRcx>%)ehjKF*^b#1^FKrH$pK3AHEkg_kx_ZDS2cLI#`Fe~-cTeTXgkeCF zBG4$Z!a-C9{Dauhv4Ultmpll}V$^g`YHjxUqDTl4KQqnhnf&%)Y_ihbrSFth>WoY2 z5ut+!>;OXRkmsq;FUCq{-q%mtx=u(FzWSBrCw4+5R0}Mv#aRsJqk)4Xzx1c413yTL zgRhCFMx`9rYPqN{_Vdq=_keZfNSGK`5wlh_=(O-{5JpwHm{@+9w#TzY1HNtb>7tV@mLk}zb z%aDIK^X2p*5D%-o>_fyQ6dxiJB$;}E0u=|7c)|YPLH{J`iT_VBrG6n(UM=D4&sp4I z`1bp+N|V>_Vdsi0M~yAXQ3vaH6EG9084CSBzx59pTR)t>K(;FNzgm^Hj?|g|Fl(DI z|Fol7=j|m$LRTuXEVqCV= zdqk@*z9iy=6E{-)0IK5wNc>nC5QiRP55sD5HBP+wsj6JmUa!ISgbKHYF;cHGpit{{ zm=X7`21wNFWBI0>4Q|dY*A^U-_1AE6IH}`TcYBL8{sUmMP$urfsjlIsH+t(d!W|Q? z_bYXGGd3d+pQOWy7EsLdnuds}%+$@sr>~=tp{Gzyy^VLa>~Q8@uX~4KKKee8uB69K z7$WL#|2zNV5&pGaPl)?SSU0uLlg&7JxkzoCh_tLRz&6%J8qDE4N%F?$GIcI8VKX{G?Wp$M8A#jZ+RI$01Uq%&4~@OvySkeE#PgACXL)2bomG`w6T2$2Me; zrd%>DGg?;XoqmZFwr1+;<=}T?r+S^AqE<`vv@`q$-GAssY|VN90vUKjvu7 zM9kxVHC?W*ZgXp*4(FY9?)Q22T0S00o8m@NMGEM?Fk3)) zIg~+vZa4Ln90k!>Tn7Jvt$mS@3m~$LMdY*X<`<4V16M7Rjdiw}5e8zXlL=9~D+3=0 z+g_*L=e+fkHwbnRCh5do;ro5g!CyLG{@v$l@kxVi>*~V?03mS4V*bk7Nawv3#BXY! zPhR{voIy_3B4!*yk$*P}O4axgyqE`$Jhk=W?_??lXMTOu)Or1n zHTvHl2D!j^Mtc;6e!l5yKj7k_>?aw!8L>TH(co(IL$+vGH+Y%nm{z)G}XAJLchqJ*F6xlDkxcK(ElX( zJ0EzmhC|k7SE0xWrx+0}+VI@7XV2y){JM?oPG7G{%%R>#Z7emj3Fps0=rs3qt9=K( zr+8q^4GVvQ3(bAnhXB*HmR2w{y zTSDmmQ;X;?0AbTn(0K=0vI=w<*Us$aqW%q$Z3~L(G91**e{@9U=xq@+LDM;fNLGn=!HI9b=V4KQ>K1(i^rMfnEAcc-&As$5fBy56 zZ!kw#7hOfZr-YT0CR+e*$RBzujYL2hjHI zugF}ITd-zcrDQL7G$f}JMcZ8W$2f)e|q2}K(s|%n>^6Dg{A4mPo80EC$X4a;Ss_2 zC;$idvr=zQkmM}DMWjyw&r3V8#?2FUENN#+xm{xD6N@NL4$uEhfRm;Qb?xvRg>3 zzdth+0R0)f0b`&^rJ|Py2RGA%*YR#{eco`0>miRk8s{jG+r5Z)$7Ap*dhc zhX{L4{Q(A$DwK>Vt(1G=G0hgq^-16X^(;z&gz(>_F7XkV&WLnOMg7+M z9u@$3Yv|lCGTR*#a*x+-!2Z_;&SXq9ve|n>26e7|t-izUU&#ulBTv`on8)|Fr%M@3 z7lg#AxnuISHT9p_`v97U$H7hyZUcQvb^?O(r0e{DBYoTE#sY>(wZ)1z1!S{-h zn~&YL+G^KLq?Tf}xM#y3Vw!(BYr2@ahHA+Z?d| zP2-Oy0>El&w|{Ni0sH;g>ghxOw!Zx~j6_-T45VS2s<%x90u1;8YU7E;Z?|rb_#J@E zs+hcvG2Y$OG-QxYH$yEY~?b_*%l$<_4=Tox#cx`H* zD-KBtJ|)Lc|I}ACp?RMh!>1yLdXMl6T+yy@QHu*hGbZEz42w@ zB6WN+D!gx*=I1i3>T=%F-8bmwI`Pj$Z*gnq*V=F}jRhB@j&3W)2kv0r3Swgk`w2G- zGruf2ECxPlKmhoMcauk^^k3$QKYY|6I@Gs5 z{CqHQPL-6J;Q4mdsQD0bLWbERhY>pTQe(7Gwv2rtzgTFXj-82Dg@i_HPARCm^UTwW zsd2^R;L6GTkz<7tJLDxO~YP4MtW~h6a-z%EbrMG>K!-gBv6X4$<%Eq4+b zG=$Ri%;Qa6FT(SP) z{h~$Sc-lBQ)`?qv0pAKH5@msFG%kqo<0#TuSQ1a_{}f)nv7)&s{vnVg)|j9v`>d_HMLKXL%k_9hRFfu25LFAE=S_S z9GTqcm**%K$Ink`SamgjY>)_EF)q9^H~doji+9cRyLA*!HnGh=?y8D(5R!XxXv5i! z051^}$Yw3-q-VHh!fC||#d(&0N)Y3p7rIgEDrm)r-&j>x>?${Uf5)_tA}>{eYr!>_ zzHD`aGHQP5{F!U!na}=?l{ay86t`=;Rc_CA@A~v1hnTui&SVT#&Rpf_`m)m$-)$qv z!gEL?-EUT@)&lsOUaV)P_?BeeAfxhjPjjaCt)vyV8RDReGpcX;L}tsbO!px)BMNK{ zN57u?d@x2Bd+koOf5JOw!@Py&UP?l+&(ZXWmMMB=A5!841(NTBmAbi)3Uy$U|C86+ znqPKZaTekOQUjbaXo+%&VRU@9=wo(dc2J@GE}B++I1Gg=;Qi=+u}J>jH>%&XFqqu+ zlItcv!#4GI$sos2fjI9z5rIN;Ih9y)t9i{@(d@=B&;ijcqrCbW3kKQZ0Dzkm>dH4a zVzT7!-$*c9aX4ua-xF>~@VIPzt*nbSd@0$j?FFoHDTV+#66?jx9L;-Id|J!QVj^4B zlOfL~#yt1)WWj@zt%K}3E<>LW{HVK@$CUDnliErCEcbAIRp(-1+jQ={yvKt*W++%# zhi(nbk4b@rK?r%fY;~HF^f5m{c~HcHpY1mvU<_=mosrt-q^+S2B>>}Kv>N))aEKi)R3OSw`<4KYyEoV`bD9mbXk zU0jYpT0`h!w5VYxQ*2RWC}eh-tVn^?c$O=osnFQWt>K6J9Cwi;2@|{*Gn90sdDcv3(>OXBGMjH__+mU& z{QkM3z@(b|-=@0{L?k6=#kSfc?D5hg_hIml`G-01&hwVm(o9wzuGumPp4qcJGX6f|FsFbEHjLa;u-+tX}H|S*{Ia| zO-Ec6-jn3Z;t@K`;>mU-UgNwnAwKK`XwOB87#Y15T$!OJ&_*yd4SeH1TV<+lx}wP} zv0}8m;hQiZ4{`W$x!a?p9`r9$v+X{)jSK@zeB0{CR;dKsz0ws@px)(BI!K>xl; zRQLM;m(`ND9Ye!S%IQ&Y0Jx>Q?$Z5M*Lj>qeWT9U@w;2EE1rA!UFT@Mq6{M!==GXv z{xsTVV>VTHX#J=}IK7UCwq?0pN~UHZk5Tla{RsJ^y`qXjSZ95!_G#!#H@syk8UV5w zPGns`RvaqCUa}j73x-wY|3Tl_&aHNnzK~L_x&HIJ%QidZwcI*B22?octP+8hiXFEmJX(c|T zKT_X3)4pC}-@R&!%ak!37iGDq6>wGVg_TKx)nx5P?qDF=W!_~NUf48huGJ_efbkU1 zo?wNWG7b|tvE%i7u{Nci9muBQw6gY0n|!CnjYTwJu60hq@JHyXL8}1oH?D}CAmLv- zahJSm>3Zq3t$c~Ez)D_Ve{)rz>nMk4eR3YV-{PIvPnp1BnPAFFxZDWIx00z@epEg$ zer?9Exk@PKH&~KOT3YgsOhq=G?Ixb2C!jd2=sOFl|op721eZ7Uy0DtbNn;=ik&-aQ zN@Y3E>UDzy_b#l-^Ys@(b*0p4jOk9&q_{1zoXcZz{mSxj z>Q0m4LmX%6Q;U78o;Le*p~^80mKI+}ZVx99bCz0sCa@od#Y9PZ4CUx}1Y6ZOv9P!s zlxcO_y~@;P*kJPl2W#*NI+~VDkVf4OHC@4#^f4R{6Hc)n$ki zwF@Lgs5Re+He0o~8E zt0G4QWRH0|B;fUQ^#2?2ktcHiER|}U`c=05*5!l1k2MC{@fCTDM^Va6buz8fY>Xy& z4v*@3yyY(F`r7^t)9$$*XUK9Nna8{6ORTd~Xr3|U>!BBX^`oaogNy-O-lD_Ae0`z% zRzIBKJkG#$zf;-5EhhNG8+qfG=0?zjQ?BAmw-CS-W-wedlk2h7(C)pm((_l1@PcW8 z+0;l7R%gg}s`=TAc=<^Ch%^hGhsmOfMcMDmvaVPdC%)9E6f>7Y+k2a7OIt_H7A$Kj z53apB`&syyf4N7n+65deRh|@*#;sF9F?-gv=lCQ+6tsrN#p#2nGblH2hPmUWnFqdU z-|YR;zLgdHinCw=hTK8$sQUb9f|5&2)<^f{xed*h@|pBlH^rNY)8&yv_iQLoh@}?D z<4I4;&b8%;N8$@Z7tO-fvOt?VY<{jmYv!#^L=dqpN3l=fbA5X=q)fi;))58J=FX!_Gm3riqkv$s#gITkjsNyflH_(h zOFX4-wwI1otU%??*uoB=j61S9=`0y1ttPocQX4>Ezt*1|biKX(Ir7IfG!lvWHqV{_ zTfmj9ym>J6Mm}r3-wuuKU<$;+>cYcohw7%IxfQ zllh^ufhKvwF&2n4t*+_xhjPzS8MM%b%^pjKqFhntyo{(s`?+uZN+q427cJD6+UI|) z{9wjfFV77d5Re<}$=OpEWEdTTNzs;?F%7OOgZPlDlq*jTRP}{SBrvu_JPX^2ok>Kr))8QKS^j)YqNYfz!`6Bw-xis*_}2 z6?@&Rw9{mA)ucFYt1D>k<#S3YC4u%QE0d$_Ho z;FQIA?U)#p(SpSp&-plh+s2EcW8$ErDe+~{fgtQjWqk?f3lD@A696+$wpp~Yfg|_u zuJ#!KPwrqiDzx!AGmG(&@aUz(zr_I;$t|a*D@(RRkcZ^vvQhmb01P6GWl>F+G9vu4J0OC?hpS6PrZ27>h`YG^hHi1mY;DHJqD zW^p}V6O41&t4?Z}_IjHIQtTL2KC)3Av*Ru*bD2D5@;E#3ieA=u zdId2EvL0(Y$V>Qs-_^dPok77zmIl=u-9AmtQ(W|ddJI$LW)d}Cvg}_?ziLJ4W0s}9 zzL3YeLP%Y`i0NBd6eUF5tgNoqmIW<8hkq0tfF}xmK+k`n)2}{4r)5Uhm@U5f3WCvB z5WB>LSeWm|S>(TV^eKX0=o&lUlb|g)|%QnXC6P*5H;ScSTu5 zTUuHyS5fC#V$bMj?mG8HteKOG5f=4JDDjBs1I!XDj8 zCA4sei8>L$?6Mtq-lxFEuzMP-x!#9%H#M!mx{efaI3E zd8O!%etaYA$KFw(A2u0zYhIo8)O8jVMSgw&B0#qZ~4So?FIq2c#2S7WQ!pxqS!_ z<<~9YDJP!kv}&wxM#s<`KWV|=8=l^Qua6e=?5TJj1Vp_=@VkTIwa2Kx#Y2vRM)%M< z#_p%97RYWh$Xri?d+vZoH2?sY@6qXOV8PJ9d;?-H%doh-id8KP-z*cV!@*aNd3H(E zk@<~3DzS1~-o*O7_U*=GAyWAOa&!&ly2x;@jexWEc@4^~ch*_|1cP6Iaaz3MywfzM zio7t7at7XVy#07bR*}Qn)$Hm_OcEyJ6h(-L*88t)a}xtHYbm8u*U!p&m_{p>nXUMM z#7g&zXe|#{EHXD}eDnt9cxLhBK~&wXp_~RADM9T`y1{GvnH-}u=(No~ppR3(1#1zX z6`lSAg(61<x} zHI{tY=)E-cl+^74v`T?b(maiF*^(Z-{d#d4#I);JcLHk_o$vxDO$Bj5 z)G}0qopCD5A(DaCTh5roB39jd{VM0UD!rO@7W@$*_L_!#x`TNyu{D^u(H&IR0TsG- zDeD3dWw@2$l)m}{js$}WB2%G%sIolPT1vWWa8|cXY(p_cfq1oPkY2``Df7@|rzNv}mt7Ow6d zPYTpc^_Rb9QfuQueqxa;if-G1ez*cek*#Z`6)=psE7dRG4D(et&0zh+>V+)Nr1fRf z@-mHUV(A{{>qNpgiD!`f2wlgwjE-YopbOw~mvGbflNic|{YP8obrK-P(Tqi#64-gC zaOh=XGxocc!}~)arNIPHMbo-}C^k=?MBxo@8vM+tv(D;bF!Hi5N4o~gZ&i^uD^1kp zg!T3CJT$W=70oH}T4PHGj1~G;ExF$3~W7nDG!>T(c_axqxszZEvKc>2C2vGpGP z`~qE@VFHpgQ?0&EEVQ-HswT_c`j-pyAL`{r?*`}x&EPzjI|@J4EimV?36J#T)BQtwq-TRp$%IVWD^D5Yt5w?lWf zxT!f*u~KN^Os4ReZ)P6oOWf0aJ=%+JeOb7~5!z?z2dC*}%Zt_`2BS_G(P0 zN2&n8#m19#5_rQnEc&?zqDFPzg1mg*bxj7t z^tvA-HTw(a#fv1!S@T)SlqCi%s@*8g$P~`T;lyBtKH(I3wnrr49W`vqF&4MRb%rlL zl__*y(z};LZDo7Ihb$=)r3V1ph!w)cZf94sefD$tu=eVk&cmRcnOe+iwMK}m843>bT z)sLKe=;!?oL7ZqeIZw1oyTo(gy{OgmMif?+>TBBM(=HV%Fir=I3Jqtj>p{wAJRn~V zYlesxeQ`euYY5|HZ-|h%L{6R-j^m)4K8zk~Q8y1-IpebmLqDbnWmps}hX4S~4{gl> zqOesx6DpVkw3jWFA#!kT>LbA|BMQVugU{-cX!hFW4vv+X3=V-UjRj$h_RF%dPR5N& z#=(xj>mAv|DTNdL_W<#xSLdf^$&O(S-bRFcb5|t_Fnk0MWt2qZSG;d8PtDZvFJ(q7 zm=p{VZp1Cx0I=Dg;=-LP;AO?5%q6dd_$*6UmueQcVNi^j@?e31{^nm=nsUs)8Npv_ z1)hWb1lmIDWcpoRL+=Y}4 z=_)8)tQP&T_O+Ss{Rg4(jU}JLhDEf6nM{al0B_*uIUJSPmB2pN!2scK(Z&Eeu<;SI zFeZGwU-VRc6#xhq0MiTQrlvn#nuyIwR|>rw&sZ zHFs&~zM&w=fuWkXntbiD&iu}^D8&bneF*nu+Y~|H<=o|wlgO%Z8V(Rx^R^edl3)>c zBLcJ5Rb0)+-A1aT95-?#?g6AAH=;r|_gQ!J98nlo$HyMe4Yq{lb?U3h;*a>hA^rzZ zh)-a9(G|*y)+qL>VrOjku!WcRK55;^cItLvCk*S#US9qlyFAy4CfpmYN8j~hqgi8n z?(n<<72tPhIsZ_fVRkbeG~ci(!Lx2INbS|jmnx1uH)+M5=+Zs5GQx#}o+OXuMf1WS zTBp3tFkPs4&)fM?hMF5Ic&laW<5dg^4qVY8_JsoP+s>&HbZdyjDH*WH zBit~Ko6hn*7&CuLjGZ?!x?nTv`)oS`UK(?0lCOB^BkE(-i-=WxEJqPcW$;;)1X0@4 z9hxNA1}0Us8)R|Cg+;BLK0L9`B8V@KUAvJuA+hnSr*|clQ?+hRK|(;-!Tj7Rp-`={ zX|L;G4VE%)C@5j_9ves#D4;618KN|nDiV9jKuXc`t}o|GLt9JPGu<}}r-6w&aLv>? zz1O}nG1726fP+(F^Q#;8Sc)(^qi*VJ_t{|-UmNhapo<(7akr$Cd(FffYAS;P0fq$k zZ?SGI0{Fqsdr>=?`sX0!Ps@2yR7VjA^|dTM5sNt@NFUKatE*vEFVE3LS|SXi{TT&J zD*c-CqMQ_h!45~G%cYGrdL7rwj7yB}YOB?8d={mXPxe(m8un%J3>P>H4+pzCgFzaq z-gt8QE;u99(lxLf!*v#$KyApHH z^KSdNXp(~iEP2nb)Mz&5A&Nx2UWOpSZKnh=>kO9$4xoVlJJ)8(z|^LeFVB+wY!7ZR zS0{HYihcm8JqDNc!QT?NKjS@3YQ`LV4O1()-_@rPfkGH6pJ^|A&`5J)wo|H-C=@~T zN7K829ONjewh=uvGtsf8t}DD^6m@;v((3vI5bi~Fmz_1i&en-!CpoT=uczLLD>4B; zAE$NAzPV7;dC3%4SLwSDtSp;=LR+X6KctbTiO3H(K}}1Q)^x%BnbM&HHvD>b71ERp zeT(Lc9ouh539VOjz1}Bf0dt4uupE!upXO|5? zYZZ%nq>t|Gj|%t91wleYh|Rp$?sXX+jN!o!{;lDWS1r=U@RAf}lyb(N`)s`$q@b^u zbjusU)T7PH)DzDhCKo)_iRj2tijkideDbIVZsAkmkb`@QEPm2DNLn&`gl|8UJCLWc zk+V7!ZX@;4%LJj(0CFgLE9$V(^7V3z)7yI~X`|_@Q&XgF3tT%ffp3*>+^LD?g}0h; zk&bboulQoN3A>+*7>WY5UoK?stWHl7RLs-BGzA3U(xBatMU;q1XnU4+rJJaZwMD8K z*iIeCAoY7&UKVg&CDa)ke)-ODM9{@DHHb6@EC$j|g;Ps;H8MH9VRD>qP>y*%+JgtQ ze9T%~YHOM6eWzxSYXa-FxCJp9qS`+WGMuL^F7+22Le+1ibRyOnyQ|FRXIjdaqU|R| zSW}|hR?8&7y0#<`c|=ecHu$WK0QNl89MKNOObeFxucsSbSO%$GM| za+rD+CuFbP((xcp-RghZ7506Z(d=tsm~LjcFjnJ~vYk+)*tLhKvR6(*=ax9y+Gi+P zJl-veedS_jscs#q$cRqK(1k)xWS>PGL92o+FF6|=TE4j~qr+Ddb1-n9rM(*PG;xuLZV96waO9{>Ywg z?8Qah0?@qr*TI4MYc1oE>POp35PZPP{m{<{U*kyb#d@&Gb_t~FFTcK%w^Q2n@Ak^p zPpoEuJL<^>(mQW}NtRKdG%|?f$`E8Hyt80h{E0!m5uy#l>;I>MwiAUw)olFo#J0n# znV&4C`7wjI)AkY|J$1Yh`_lPk1qAG2XY!aYbZ!?f{c`)BeV~4`=l;x27vd^V zEvY>@#Pz50*~?*oKOb!ibCkBeA4&rv$lT8f-S@Z0`Wr*T053SsVRv?J&x0Lf9)1m@ zKh?Jmg@XI#r+8Gi${D@ZKxI*L_Ih3CZ^`=Ykwk&<8gtaxZ}a1rI#?F)pA-0%j-8E| zIWkfouj2)VtG@qx6bVOt`o$r8~Y*OKxli2xXyaGwh09eQ-YfCq?1 zCchHCvu9!g95YXr+e_kh5e+rDzSJwOk?q&j#!f-lIZ){!#mcvnT09W|7|>e8ul6Si zLXj&YEP_6RUIye>47~;SGf&A1Z57D=lN;Na;bX7Bt_{=PpnE%xFB;r`4U)1|!2Qo% z|FCQB4v+}l#f#`|iJ&D*M4!BV*qON38Jb9;JyRsVeawqgf2aCuYeW1$lJDVi;crhNUEx!t?C@5(~Rkvz8yxdRLmiLJq zQ>8rUv&vV6u4p~8JKwzcS%dlC@m#z0p59jJ^{<6!3f5D%zmJ#{&&AZT#XJhNWl#&Y z9j+g(Y1ZT1U9Q17fzB*VPW;BY=EkydKqg3*+?6$A5_mX(kDdk-lFC`Vw8Kw9uQe(d z%C{5G!eL$zX0G&JuhT#BuRk#Jm_lDdIjR=txuG-@o}j*}Fd(Otu6v<~r=!F-Y^(m4 zwfulBq$rRkOBh|68!8OXyt4hJ9f95XRnmqkQxKlLxi(nieP=lezWL$0>NzuT{=E6@ zC4udFbT9h@wxUJYV;cB8xipiw>1kv4-Q~L0`|r-ujfVEp#Q{@D-eQ8m(?eAPM}Vr? z80~!jm<;Qvk6FlLvuE1a1ru!>KZ`;0bG(15$-TpyGeTl9$Qqw_m2#E?1*c-o$CaK3s1R>nZw*d-s}ruh?Uc3#WCL2MY;Ehk&L`yVMau zVB;B0GcRsYwR|NwuL4e|VHStKuc=bk!t_mb_d!ZFQ>`sFnC8I_+$6r7iS84br2UXK zlcVInWn3kh$*S8gZy53iAp_@5VacEAv}ES>O}A#nR)dw~jejE0f1t7*)a+IUhiyBm z|LO}XFU4W7P74j#r>!+Aon;n%xW%tBIAF_$unHfr z6!JHN7T$4K!>%9L1N&>a+*`K|k?WxYr>?5f|M#0amg;^MxSp>qrQF}T7J2sPJzlY= zYNz^Kp?UpAM;{J~q#l)0W!~*!uhL^($F%SxAXm`#$T^Zp_J5AP)GB+GQo-arv`z3F zE(qT)u0M$BNiUp-4zdhtt*!5kwk5WjuA3$FJx{{)e4-zF>{ z5GGHM*qzPbgaO$n<;VT(NR_s&Bqm;<_OT9v1s?q!D{@{y}TdCu^1-Sc>9Ywh!s+hxRFKLAX=w#edsvz^-IUxcj~l;$&8 zupIw?VZb0zh{ZxP#dbHvq36N%XQDpj_9n{P^@44{)7?O>K6cq%_%7c8*IOMA{*V7= zO$EzZJ6F=@xnIV;{p-UwWF4QPCbyiB-42oD$wBnOwEpm}hE6i^1Xwl5pwEy&asO2? zVAcOE;s4DOTmfNeV1WR+S;2oKF%F|krnV=+^M)P3^(o zY8A-LUIk6S{>$uEN1QZ+#l?^sQixB+$>*IJzB-xv z)wUe}6DMtJbz(cj#m!4%M9Y%ti=%?#+*$C)YhPpLzBW8)i)KS#2x77cL%EkUJ2ifDGpwFm zHsRK*sHFR#^s#9eBG57Gz$S{?_UlVeEDo zWip7dCFm1|mjPBVGU(cS-S-7{6lvfhY%j=e+=Bboe)poW>LTEHnk@nm>#5NCo{L z?7ewBl;0aa{Atl9sT4)16qQg}CMHV>l|3OdmLxkfWM76j3v7n+1D{iNS3jW zZDz{EV2rU0gE8iJtMB^x_Iv(({&-%`e_rOEd(OGewViXW>-|=dYSNysny?I*J9N%# z&c@cUZ#tRge01@LH1zZ+J&3WXx-u?#mKFT=NMY*xs*23UL^QRy_(mw@j8)(=D#hcv4`|PI8GV3i%WrEX0Ke{oX<8jmxN2RyG5njBqnsITmiJ2 z!VO!}pV}c|7rGBX90;{|_#TD(B6z{;vIX8k16imsd+cDR2s=TFC6R%%I}Jx#KE%W!Nv@ zJ6-x<7|j(DoR`kNj@ON;(U)FmmFkkf;jk78#OnGDdsqWlnitl8L9cEcViJW$eP}b2 z2#5$^yllSm=x10m?v=Cf?BfE_dZdmZ9xt@vwCvNnx=~1jW{7C_RX&IOI4qKXuB$z& zv)-_h1QUX{527uSV?jkz?pFsr@gG3@>TFwf=zrq@5y%bi;&EF08_b>X*Lp`4TnUA` zxu-6VCU$X1oARetW{R<~*YN~#ZIXqEEb`-y_2j$3(9L-=ZjvNpEy1mvwhJF2f*BUF z;@~WDt^GM59?VJ2ty}QYtr*D@^S{1Aw9)G!Rzd)Ch8@OlZhq^UNl$~in=myQxQ*fN z_TSbW7o-l8mal-QQ2OE?2{tqHCARy!xDOXDDe)-}ZS(u|#8 z4Hxg=Tz28-{Ab>{`is4<{1mNM!|D1_+B#>fL!hQ zIl4I)bLbsXxyycA(9CZxz+Aj@qr#TkRiPoYYRY9NUMUL<6FE1qLTXRW zRRH@lU)K;lHjfm4;&>OHmd)j13KUdasqnTNx_e|BaMVg6`$CfpCZYq1<2yklopKYK z(Ju7~46n*E=>7MR-0!!y+P9CW`E7U`ro=CY31Uh#+uMt!V%zJYzMXHB1z1&RVb|M{ z!E7n}WC%yYu)Rty>jR%Tl|z!ojMpY~4*M5M)yL%*2!D)sbV6A540^b%t;Lf*WH{<} z&_+Ht#834Q_g{BQz4ZcrNG-kSSC}RLA#jwNjkS?ke(Zwj6q8^!rf7Xp$m7uWzlz(& zu)2LM!srvyBY{w90Z{dzYk^nGS;Z2?o%oWv^JZ=+8z+VpF^8(Z?mQ%n$Il}ugeYAD z!BJ0#f{F{To}3xDvcyJUPB}zzXcr}QO?Qu+D1`1oTkxbw07qAPIk6=A(E&d*sP=IA z4K6cwCsT2EUPeV29`fx@-}lDUcY1EAGaNW1*>Co1;m?D74@M8Wochv`Hnq?k>f_7d z091fyzEJm^G}vk)%%ZvfJx|dhQHT?o|HJ}7ysykVN7msu?G~=q@91D`ZyaE^_;+SE z*GE6w0qi13tzM2Q7m-05CdHi3<4zaqtK&@%uf)P>%Wx%uWan z@%Z+S=*5lqfMvuL;EV;dvvx@gUw0(gCe$x?X>Gb?N{m?5t|X%Y*1G7!#bQs?%m67x zR<;5d#s9a==#@fMebdT&j5w1YyPAW?L%ib*Nc^^oTPl)ky! zTH2`)Ex(F^isQAs@xkI%`TvKa0NoC+tbBj{0do++l!=@yF28;e*O=^P!j zr3arT!x9bia2A+7Iiqq;Q%5a%BISWZ1<0lx?Oj)fLB!268JBu@KgZw&MdG4&eksPW zU{il>Xf`4j8K65UALd(oXp!Oo~~Vo^F}5hJbR;GFlNiXO;VoGGd@ zD?6~~qDX{|G#JEK%5y{g+~P*X74=*UYCB z0s)2}qDnt5c22Y^kF%|XQF}f05fyd856&ZUd6?chA0;3BO*X=cX^Hn+ruUeXx3`yo zNK}MElrs%0XVHhoL@ceT|<5=)m=+; z92K4hR+VK-V^=0wS2|CyDuX*yJ2$Cb6B@*Nl}!e>N8lJmS)Y-cd)$<{2HXUR?r<>`fNwD!>rCF)OD&k|y9vev*-$1o4|>yOD4EJ>wKJ6lQtw8YZ1^!3YU3iHG3 z8215=b$mOG1ZnLw0icAAA%Zq(nWHPnwNIk<>$L-ed~)?yXXba<<_cFWL1N7o$DxlY zn5gE_n;dyI?COB8bJaE_f)9!F6QRH`Uzic1uaRr?XDK#|sk{Lc&x&69ab?nnvRO|o za?I-mvtAD+)>(t8^+AsSm5%jfGH^M)+!c;xH{^7m86Jrgw@;l#l>pSADMXAMe&-$n$aG2*vYap-d41)LC@@ydy4xsd|`)8e{ zl9ggzOCqP!WSRo2hG?Zx{#{c;=zJ#2wa&F_Wo03J61>`|sQ+?PVK0Bh>qT%=DpI7F zM^825JM-Q2_>Xw2Am+-l5e_pG%saZ!loH>^ep6)evHUW&r+jLG@zY3VxcMbp`C)J! zt)f-Bw__txb>-``dJ|(i)p`RF5XpW848_`yTJ+!YCfI7xS@V53?7QP-NJdx9rcSDa z;R!MV-+2k^L-J^7XP7V1d{oy*qvz-aI$bszu9Nd?`VptArA^T(7n5Xr=$s8IJm_s% z7j05TmdHoT{P5$r{6yxGkUyLX=VqfTG@j9A@Bq>%zWjZz-5aVuc9?mY6MTikehc*8 zo`NU4+b1qr)X(KGK#iGN@D;m-@U=Hqxz@YEV2)uhd4`^@t5ZhtPn!e#W^&S0ZZkpF zZrGe9PC^2gEU;0v-RcV-fk^-!z4gD3FB%>~*YX;@@R5`@KV5z1U=`_Ya<+Q1$Hs$Z z!XC_D-t5&n+qk{g37G=bVJmeyGbz6BSfw0unR3#ViEWAgxDyd zMmMm7Te{z#ee18h3TX&Q7GEL4UDBGveRGp7!pnOUL=%_BbP_91)na|%|eL?(7ui)L6 zk)|~Lg>OWgdh}7o=lq~ePoWC)o~8BY0-p#=v6gv*%lH^{6v*(Y>YU>2OasW@I(Pkx zWL1)f;rx=b`gP?^|NX}#d<0EhRYs&F2At_!$?FjD);JopH`Ts?;uHiScU{f`(i9oH zQ>S)aQN-4^7VXORmkkd~Z_K1jz#G_&)OZVc+8%z)Nn%dl_Zy(QjxWGYUyoV_4>US@ z-j|uT>bFkcST&mE5o^;iLcqqQB#6$#N}9{{UgR$U8kAf@#?A=h+ul<4ryC%_X$q}2 zB=zS_51ad$nby6=h?dpX8etqeaEiyy)LTgvWlIgbvKCa@&Z{y*f!D984EM>=MT#9F zlq>=W4`9_VdB(F~;c0Ywz3p3{KDGLZ8TWidw)0*gb9?`enJb>bX)drUuT6tCrUmlr zJ57jCfF0`G=(tjtqE*fkVH~F2&cFP@h&377)$E;Oj|?VoV_ZbBhQanjYcn>+MI2vQq!E;colY_iqa`7sXD|f)d1)Bj9s0|S_ zc0*ye%_Uq_`xY;=N?UzWv#psppEM1>;q%L=zbjk@?n-U9Fe*lZ{>P{%>-1%$XDkS9 zuLd900%KG^w0CwqJK~%Y~&`(PP9}>Q9I+(r8JM7--%H|Fiv~+$uz7kz%os1GL z=%a>SLj-H_JvYAO)9>8FEvehdC07*(G#~|tp*UH6n>II;X`?vH7k$$>$6>J?O|??) zP{F6XGXx?Mg^nFu9~9gP)CuKcEs-d5sZN=hiKyI0ICu%ik9wbOJqpW-`zDXTW0lteSsf_Thc%DI7u-Hu1_LUsFgyp+9NN&~Ay0NZz^c7<_<#LYgW2aOb zwpULQAEXTGe9p1ck$Sbg|MPMA73tt>0gYN#1~%ZdFtSon-{^Q)B&5%E=t*R0^xjK^ zC%D169><`|lfZ_c#va}Jf+tbzA8ph zwqX%LpL2|(x!+<_ zBFApAio|hv%D8a3Q7gAl(^p=2z~)CvGR8~qQ9=9FLXD~$jTO5%0SSUD-kfP)2xEM` zIo^V-tk3^xdSQJYlf{mBDY!H65yb5N5`|=mG7hNUw1Hf_T487}bHpRL{(vQe!C$?J zL-?3WLWFc82FI|J%^Kp8C!+r4;SG13wf}yIDo;!~KTToM{GnB!nZwXSXEgOA&qEiU z)0ov6erA2rbcwvvErDY3x;c9&qx#a(Nt;)i(cY@HAd#WySL5~e6ycT2eM^N~?PmEQ zgCEfM-HnQycOxut@2NUceieh66cPEJk@(7~%cM*%IQX1Ym!fg4SHmaKK_PX9IZB|3 zFfG?HJB0c!eiHonO5Y=-5Ezlr>LQ#qSIPkTBdnrKQPHsW*a*wT8(iR`C|;5e#^H%W zM36#*yo%GTUg3&A(K~`2SY4c5Hmd`q8WY15f9V?g#e*Dk+bB`?Ym>{M>qEGjpg4V) zZ(?dk!0S?B|FW*9TH>p%d0hrgKjw1%*UXzgIgwHzx2HW8W$Ff;V?E|EwII&7b zLlsT&CSLZKxfX$+dTcT zCqF~qZ-M6LNvts54rK(umuKjmHS4PM9KC?oj)hX{T7lS$w2ti_{szxth`Y3)~D*92=}9 zevbB(;wZl`eH0$d*wjxaWv{0?MG)mOSUqu%8Y?zcCyD2qHf;3amLASf`}O2*Lh(A{ zK@|HQC#r1oPa)p^%LgNnVB|qyEe?=I#G{EC4Odxgb z;r;?&yza|*<5CpHc#nx9#ufW^+BG&SAxOKrN9vv7eJSX{M}wIHpd6A)2Y0{ceP<8H zTlNvDT?eS1!gV~%q{u$}uft*2KvmU8`&ZiHrfy{5U#B@1bnVK9sOvcA4QI;lnM?D0 zV^uhYIt%t$xvF}xCn*_+na@e>5W-eC+UYxo=Q=$cp^fUx@O#TuyeHU6RZP!I3q!s# z5|`?ex@+G)5;zYWtFV&9HyAe>_%RNB1O{OaU1d3ipVyG_oMzc4yA2Gwc+0F#F|;j% zMx8I1$~xiiqiU^6Z)8D)UHAnUr@I zEQ&7f+ohc_F)cT8vtUrLcaaJY{(5%OcQE;&%hr*UBXBeYc5s3pSa3SblKJ9yxV$&+ zuef?f<(O+ysa2hKs*n0pg`NTpXMswP?g{6fi7JI3B>{b5&Y2h^9*qep^e3O+eTZoWUWwNp7Z zd>!fP)r$*}Qg~6)B)GlP;^Po^);)PdBX>}X-a9Jr<&svM-!sFui&nKS{maT{CaC7;0l+&(#flFJ!AM{A`xhP^? zRk4mi%D8C_cg&GsdWXc%oZGT&@s*0iIU%%E-gI%FQ^Zk!!_l~#IqN~CxHqXP9X1M4 zhU|KRf9sCNtptw^jXHMP%-XVV?yj^nDkCLVgCz7QiHfY6*9eBVnpj!5p3I=gjW$1t z{28RuYvKKlE)mR?Dj_J?73}*|ppxp-^TfJp9hI{)xX>Yje?<9-lwl=#Q5*VV@Gro} zZ4&W7qwAW6v)anc;0UiS;7Jli%0})s9x-z6;l~`m1>aYCc6IdpFF%4ej8B~XnP4}cLLsqP%)kf%6aG4CTfm};%d zq#cbM{o!c_3-LpYj~yq*80seBR+#BQT4UF8syITLoMHoJNBagEe3%QrUS}8Q2sE@~ zXKj}__k`Y2OS3lYyqSevk_1lcl-!Y>2hY`Q-Nmga|NSmbmV{8K9{g@M7pwiy?!&eY z>l}J@V3E6US%&*AlZ1J6b2H*0(Oo8w%A!B$?M1@##4leLZy*>D%PrV;~ba(mT@|5|jY9ieM(pp0+IoK#}+UEZ-Fq z_u%`3_`ZII5gyYXN=)BHuk>?aKGpxIP?_pYd`8AV%$17RxlWv}%IAAyD)zSXu!CBN zj0Y6HmCGHAavuk9SSdjRCpG?yuvM z2X`F~9UtjODYWKIm@g%IK3mp#N*%BYmU+`P>K1$54Re^wZG=AApS;^YD?s2BZsvZT zj6K35Bupd6Bx6ve&<_HJ2f8*8EjPTQBcUCM4c31 zcc6olt;Nwg1-#`zYJK=I@@Jlhau)~qkCsn5etbk8y5n_OweP#qH$R!`)V8p!i>+*x z?vI6N#X*MVE+`|Dd85xKm*<{63#*bj6?8!BO`T*8bstap<{CwmF8(s%Wyce6 z>MlbVp6_LYwx`HT6;R_^u3*>XWhbbkTm1B{U4XCDbKh}b^ky*_rGjs0VObJd0?eAk z*srO~bbjVi^jv9HpsF8t)RI=y`%YaRwlDjBT~}lGp>0Z+Y~RuOG3W2VBYO|mTXzLqo|k}P zWf@_AVJ@ph5Wi$&NuS>V7M7|K``f@MvY57;!K@nF^65oQ_^qByQs~G)DEk^fWM6Y1 zs;HE0D1jJuetZpz#oq<&YjKR${o@g~Ff*-n%+|I~2M;zp|Lyzw%iS$&gl*3iNx&Eh z$x6#$B}>tPS`jFx0a*7}08afw00z2GP;a_N`(HVZee@>o!b4(D8uoN6`kv~-bPE-~G)%<A}&$`7QO+E^Zqi`UI2d|F@O+3!$_ zem2TmTz3&=oh867ua*}c<5p>$jHNMPWr>3QGoc#L&pX@J&H8XD{5&^XbgHWiq>SjD z2ZKLf)KB)ui<2t8)U|z<)=Ulxo=-C*rdtN8{wS|pK~+bgjWb6sC}n`HT<@&x?|bK1 zKTdpiX#e+K9`v%TsLLzW>W-V+Qbw;e2ubhG#S0cfJdCwzyM*h3su9(Q1*b?=S)Ulv z!>0<=qE4`V6Ts(T-DR!x2FO1i+>aQQ!w7u=I@cJQmK+J8R+EB=wZvU`!>)iig&cno zTSeIYac|}C(tXOv=DO)tWziae4uap80JMcr%3anmItrYtdVx;@x2WY2?>F0qV&j)@ zXX11_Zk^gy>h*fYUfD1>2WC{bQm4;4h=)m*@5DZPR!B?pDR176snG{=*$;Itse=`| zOxEpO{i^}<1Z=?WB}+8WhGLBhLqIVz~d8jwzDq^fs+EyE+6;IBWqb!J2QfI`R+$zD6aZ2+1b z4U=8LmRH+j6l3v0=ZJY-h-ZTB)K!>lo3-@nnYy8)lhxfL{2umE%g!gMN>j}okghCJ z@T4Q(qBW4=)yTgQ37S1#v$768+(pFvRm{#9Q2~>654PON_1H1(yY!L?`M8E)j{7C# zeC7*%GFJ*Xbl6E+ByzR}x}%H7a3nb?7>?o<&;zesJ>FaV3?#%%Xb;nOBYaM2@)QJl zraoqhJ*K|fU2ir_K52F-SIq7|d^Jve&zi)y$3P~+ts|RViL*s<8w{^OPvA240w z7hya9;^&Wa%Om)ad=w7;9Tcu=vl3Wi{n<0Pa?4FWzljtI9)GLsp(h_EO|Y>N|YfFEnNVsCuamVKCgk?MF&jn(!m#Aw<6i zIKxybqMWOX91)DH-1mVa;Hmvvf2AcmD(k3UJb-Km{43i5>J_F9sy{GZ)P*s$5 z6H~y%+BWmMKN91;B)u`bb_OZ~ z?s67CHy5~k3w~*a3wf_K)FMZrQ+2Wy7KHU%@j~i@OirF+rSH{h=uit}2b7k%cA1Koo*Qr&_$;XWYmTqU?R74NXakNJP z){{=)}#t$bLf13z>EJ(a4SD~3<6!ro>gC$=@g7VP*GUbMB8 zyq|d(lmn0>ETvHsXZ_wyR14Jc5nK+*R}Y$OgCwC(7YJ8ZS&gbzD`zWKM?QXC+ZxC2 z@S+BWi2ig^rHuq5GRM8Gz7uu$CYKJuhYhjF!>HT_w0yV>a4&KKaZ{eIxqGy}9;{pW z)EPfdvu($ySP>?MD1b|!K2Zi+8g<^>(00OO`=wPTFB`h0zH!~_ac%0`O`rls+Dgfy zx1ZW@){flqWH1LWbXRO^SRW^ps0~)*|p!klAYA~9fAiHWD)ED zHIuLQjvCd$*=RW0RVl7+?ax`@>+yaqit+7Hsz>LDUo4Q9exTScxk*l|~@|1LcR{NwtZM=~a>#VZhZ zDMP>X)BI4@0fmg8?=K4oEZ=BBI&UsIeVqGX+`9lW+g+_M2F z)dX6-K4NW-%MoSmJG0)PMJw34Sd$hTwt2VYYzQPqh19qbM14}u+&Xn3m(l{1Hgv$3 zGvs#T=N6*pF3;#W0}qy?xlWlKnkhz zSp#|kz*2qhuZw=`HEDMNnz_-{Z9CoR0(puxrETyjiz~@cR}(iiy_v=z{mA#QTA70) zk>d_;#yoD+)0O_yff;tr*49P=o&I&YR^iGfP0y?5;J8QFmju#z&IYTv{~9V?V;@lV zKqtPOob2hMB}W7aIP~!IF~az&ZnAX~oUk*cTRNuq|EyzT)q1YS#2L4b91}6{(ob|+ ze^+_xgMwtPu9I`MbA)S`w6A+Jk8kX5E&-qgq9(6pX2&V#WKH$rtk=(d*u z2#Jy%HOV?di|KBFx<3HZea(N)io$6 z&Z8q5Jk8G>w+0$LNABJj<;E?Vxc`tXei3BXE5%HN%t4^K5tUayF6jZS=s8q3H>_Qg z7KIvjiE>Gp^8+Y#0SZ7Ux`M?~)O)Sh6(Ujh1eOTgpMie6VHJ8!!`#xjb|9sPu$Bfq zD)>=S`Hj>aQ5P4TGiq39K!Xe;4Vgowjmw7yc;6`68fzjb-e(&?j*L>2S<1e6^$M?xs~kB@M!z&C;NN}}`JpwOO;+o0+$ zMnVPRv;^4d>_3$&yRF41Lc0ib+XCUIt(ZS~XtSNbisIw(BEQ!OvLABoTXU=e`+KM* zjEhiK#LZHrzx_IOG-8Xi+3iVwnFXs0(0d_hUWcRt&Z^llXS6u`+*4;Z=Tnhm6Px3c zU7J|>K=*TK)re?Cmz8$uWUKRufIR%Gm`F-x8W04xYWVschpvygdu@E@F#qAS5_jSY z)UN*(0+7Dp7w&z=YPty%UtRu3Q8O6WzD(5tsH7Fv3EdKg53W|jcNdGlmOg73{Misd zIk;hztT&JuQsf$`7Y+o#{>^I@s@J4OQXUm*@Ak)KZMr)PiWk28zCtUMKU98RpND4j z{`Y1BHk6Lg)K9r4Igq&lIE?w*p609Ew-&z8Mx~8i)l!XtoNP&F@SPJ-8}N0&0^;xN zAoKAolyJ$!+h6Df_2XbyAH;w9-BwI%xVd~9W9hKpqrP^Ok1s563FBhy$c@c@s!6dt zTiEFgdpSey(nf|Wh8qvNgZ)+;c%;s5RbK@z(RhhPF88A^?&&>L=I)YRn}Bq5v$#i5 z=KZ?HXREk{$Ki@@UJ?%wSX>@bX3uslj?$e|D)`%g+LxKjGa^uhYVTxEzDO>%Li5JotR*?L4CjD#F&JW00%va=>%LJd@anOa#vMvGX>(D zYlv-2KefJjivBiM$Z~RE+$X*eS3K>JpW^;)f!LnE4#d$875hjpLNR1aXn(!HASd5; zeO*Mk=zC5+(9JCI28SI9kN|S9N#E{JTXLN(d-BV*>dxspsU2Tl|J+|qq20sEiXi1p zDo4GY;WGhq!JnVIFcm%SY{>P{d(L5_W7*<5%K>MS8!HGio+rEnf5pSD7Qdo19i_tN zRQe8#*-+)9Z0vBkF+avk(s(Z)u@;His;}`*4R1-M>1GA^66B_mVWKk&Po0HZmE)Fu z2Dj=rM@~me83ys?j1_nDq_h&5$4E(BeZp zV&#l0>?5i`&A?@D&*UfCCgk<3C{zD!WurgIcF87hhkc?L$>HWeG4W$#($mRsuTwyz zoN4BS&k57;n#Zm^EWM@xckl3@elmEgAb4c&mCeE9L0?Nc2cdVGUnRe?oZ{iHYFoTn z@xV(E^QqFUt249WUU>)N@yq>J*bnP8lJ{VWER6g{&8z*oT;lnepY=YPeFgAP$h#zN z!;QquBLu7JKLn47S1o6frE?VKQAQ&3Ed#26M|8m$pG^cVhKUm57+A z6sO+6)eSUn{>X{7qGvb?0#29?8U!l`mG5*mopDw|?#_)mjQN^lJJ`f++douSPkFid zyri@*@;FhqJkd`IB9uL>f#K#7u1%xqilQJ!2F_>eCi1o53(rU^{vq(`@VQaq(xp@B z;AocF0NF4~8|o=q@G{PxA3GO%YDF3F?RQ2ioX(W#N||6*MK8jJU?k|maD4ChxQcgo z@aq{e&VkLxJdq=HSPt|fWyOLXcb;$e*Xz4yX&A=un%e79_YkLC4#vK*ksN5=0Gip) zQIwTIa;mI*X3Nd^E8QoA+MJGSn&~CjD8#Cu374cTC3pKvr^&lM7xmqR+Y9Vd7(=&K zYHGd#ZX$Dib8$8X^suW?$>QUY*5Bqdxa?N-gRF0IyM1NH!z9x|_Z*!m8K4v{O65Jy z1RXaikj-4XRX%%;q)+^|b91O$p+{F7e_-~Em%h?S@`bIEzuCl^*bVwSU(z(pAdj58 z51j=>_!^zyLZDiinLsz8?PgUjAlNwzgkSrXmB9S=Z;U#PU@ck$^e9#w)72hF?NCwi zW2<;WyS}#2U+cxo#hOs$=lQciA_^c9!76AOVD4t-Sw#1PQDI|Ot!wc|!x|E%PK*bw z^s={-OX~uX*xbU@u86s?i++pV#SzHw_UDBnYq$0@K%%@>6PK9M`l?q4FJlSdVGzpQ zEv5&jHxgiphuIu)Vyy#m!9uBgp15ywS1FlVqyEet3fu+V+3G3)VDgq%@kVL)phg6X2#YnIbrK*FjGXI)fNh0!86O-o*4!t zPiOq}N6UMO@JiQ-f?^GsdvaH-i(N;C2EPH@ z>D~y>gWI1QoR;!@YoLCEeiDH35Pt1bj4T&(GOsK4DA zUFlLYFxK@faxd;0$H{TYt=%~H`EmOMh15u_Y5WSQJ-WnM|&09isbdcDQXXi=hJ=rG#wp+WYq$m zCe-6ipjMMUa@CQP!5${24i3)UIO|#8CJ}U;IMN+W{=y!3-06L$I+E4ZAd})YlKmrY zbndpO`a!BQNx4dzwC6CM_WHX&K|Y>WOSm*(%tC3_=d0rnQBGi~oMNN1k9pRxv!swY z2s?bzyCQ(l>2nrK;_O}Okk@{J(SJvTkCUQMJ}O1ylev%}Rfv-J^QkFP?%sp3$A|gH z7Bp+rqZGjix~0=guBJv7{h*!p=dRxF#6~ZL?$@j+6c>4cOnEkR6jonZrVefNGOx25Qcl1Vl2woz0x@Twjxur7AooYnxw9ZYD;(AG=Ox4w^ zYZtvWsyU)j;Xw%2bJr?!(1y5Qy~h5|aZhitf8HoAm&)el3Y2``8(|S6ij022#kHweMa5`u3UmU2Yu>5}umC zZzGBdJMQ59EAYSv3;sbxX1s@E&w_fpuJ!5nG869_oQUEh<&t%uyAs?IrHod5F2h7c zOXi2|t-_>07P7R4I?rctg`1I7fQ4D?2k`@V)7Qp*Osl>JbuVsJrJD7f+#c>IRUL`b z#nNgIO^EP}V*|!@LGE9d5~54s~cqfau6F;_fHfM7;)0`&;AMt1Gttcg_b}U zvnupA^V|)9#d^uq3eJsoKH9F*Udhlf9;?JOdhHKI*Ut|v#vgMRU5JS6jTc;qNPx$5 z!#;grLwV3x;Ux>jFA=P-2i!$lQ$2fjqH>_?nU`Qf*prIE{TZh!_dB@0uvMOTE!lf% znW8OqLNm?t!CGSbKxG6KUPt|54BaMgrOKMnU$V`fUTvm?+?HJQ-9#a74+G_v@m6oI zk>!aGxcC1t+@j00LEb1ymX#5RV$$+Mm zIx0+awN)4XZHK}jmUs{-a2D$NRHVnmucgOg49Gi~|@+lj&Yqh!02fyN= z^2LBH1A21-PJ)1F?xWXKI-^n%yv_|pYn~Nzj8VQXU)U2hl_Tp{RDsjTXeMS>pmSkt zWabVC-3J&M&8fz&ETc#8wm@Ddw2N04_F}{(Yk)l0i`|?3mYvdf;DD;d!w}D)jc7Lb zNG>_Pw@2YX>LGLcXBpMF_85C7B%*QIy?mxy81Jw$@@*HsM6G1P!^Lc{ie8=USa9^p zCQ4a$I^>LZbc(Rk8ry+3B+O>LlTvrQ)*#cuESPF3;cAB1}DnW8F7RG_F&*vs8-B#a(0G8S*Q((T?cFSD0EU zU_#0s+6nP3z|HCn3m`ZU$~9?a*L%h!pH(vNf@Y|%`ok0B_D@&oFXL43@s)3s%V%Re z&x*iJq4P6#w67ITL_{&64zXXS) zs#nnN{Q#x748Su;DFm7v{>x6lmWUGhJ1*~IgyYOyTRlttoJ+2jiQzJIeN1+%npn8! zU)q>Lj2Oqbn8pvw^h5WdZ3;8*#&;vnTFkfD7rYN!)t~{c?LMqgkkTsSZX-z^T3!mH zqK|lwz@8nfvoNU49Q-$FaB_aOG(@gN4#dehHNrYcTK zDuOB;v(tNcS-TzL|3->$MYrJ*^5KRs8_;n}q_`C$#c$JVI({_U70QY!MSZU=k5Z47 zZo)%c6F<}~-PSJa#rI@k4=~S2W#}LG@~Z(#p3>uF0xL&6Q6H^(m90;dO1LN_vOCI&Vg^smhGIwEn;9fxyP=WIhHZxgA6^JD}tq~{wlsd^!cV# ztE8(Sbg!s;H_WQvBe0c?lz)C*Y%jlZTGxeAFe_GVudp|bmbDrS|inkTLJ zgBG#47=9U_Fp>oBY!v>c_Y2fSml7@ggV6Z5lhW&={)}PAuNK;S5(xHN5d+j<-vYZ& zmVKm+4R;UPKm^II#Vo#FLUWf0vEssJSumFc!C)*zNNJ`o5IM~J7$D+%(KS&d!1Y{( z((8#Rix)_@nX?n3MF>H9&0M!G36+GKyrLuG&4Dfb#rWEQhQ->^K7PCDu(qf{9q2md<&P;{(=_wB zhpLv)s*P!4ue5|OEz(&oz*U|XZ4>fvN{|#andC9~*zF*tl}TM?AwFe@^%7Ob>t_wx z=~~d%NR~65l`n|%OYanawc4RKc1IELt9t;-1V_{ z?fr_tv(g`Jq&NW6wn$DALF>RBUtqEr9|U5!D9L5xWe@fj+30hzB(RcJfr_<(8-ql3 zrS;=A6}acF2$43xD;83QCOnpYgn*@WiU(HLY-&14n6Afwv$@BD3bgkP`pL<7q|8mR z?n7I@*_fbG+==c@D?0voGjWTJpRzLHgcU{f!vV-9o4>e*H}>QDz|WE^oDIC}ZTdbR zH?sH+~{=vGoNRXU;I4y?A<2`1_{*W8e_6>BVg_+<>AiXPJQDJ=+ zDlN<$owjN-e~q4IIQFH>RiXEAa_Caj8!hgl0Cl(SkEQ3YU+lE1U2|=kahnx^P);! z?WOVfk0lW^QL1a@7L7jIENADS{kH`ye2G76TmkEnL{w!)ELT?2rWTK#Yo&N=77rcH z8aar)XeGBdP8{jdJAbbi;j=XOZlKc9(Ne0l9VS#)9AzQhBHdx*Mj)uyukb@2bbavp zCUB~9UsMPLsy=2%tg+|G!fq;Di}yU6MiF$O-tUZWMOWA$TF?-3r{FLJ2ePV?%>j=a zDZvzubPxmi+EtBv4{zju=&I{Q=N6-4)GP2167vqOQ+eeBwD^; z^e-;co%b()g44SbDRjXwm$I)9m9>!?eVd@p>h?ihFIrz(+`}nQ_t&U` zJqPW74t8N=4eh*0N&oU zdM^ChhKug6|L&6lIDmbZjR81tx~?0+{EHkw2K-9mCJ&|ww!Y=jAJOn&4-ROr8%p=*r+{NpGnz$=LZI{UVm%DVy6h%~i2 zUyb(Sg_z?n*eU}C(jWiC;F1;M3+yu-OBSt3OHC}^**vP`jSHMs)C$cAkJ>NOu%VeO z@mmmR1dgS+7vUerGrmPmdvy$+T@Nx8jP_{X1`|H=3~a|;k#@fBeF|GDhap3CR2{pVx< z{G!(x0QJH2I57U8%HJH|p}>wrpmzO8+CLTPUt)RkSBTQDG;IIh(lBld#J)eVn5zlz z-4c7)|1WmHI{rh9+qVCIOza`2cj148ALM9byVYZ}3M2eI?U9?1BAPq9KY z_o5||AJ%r~>SJ%DGtzpqGbl?HAe!sVC~Nfc zvvahzXxN9QmO_ra`t=>gfsn90=-7gpQTy>r557kQXk{yE|ItI}JzxlkhSFOj+umur zr8ed{-ZT9nf7$FeBfVUXTFEo&eTm!Lvy6%G?%+@NAQn-5^Ra&zR|xFLG5pAS-z(T> z@{d06L0#3_lAFTdcl>x^kFuk`y-oMt?Qf2lYen9cK#DV{cr4$e**`7lw(YLmflLDE z2USK@r0U`1+Mv6pksYdVzVa+zv7=oAJkTf`Vp4VBA1}SNJ#;_XhU;}e|8jNoLZ7?c zhJkE;9NzHDe_Ppa_oybo{UvVDDr(bn!v+=WR-2-4U$-|Z^qkDp_~D6B4FA79<({7h z&sn+&>wB9!5j-h`)e_MbObZgh(o69aQaQ=cg;+7C`((o3>i)^CpX@$!$$Qr42kXQ8 z)gDMR%`}dT8y?Xzbo9usL0sPWA zxq3B?s}{>HvGNy8oRV&}CI#Ii)LPmDs$XIEF5GCP1f6oh)JCZ; z)OO%Y6*U%!=JqZV@$WZT!tuOOAzQJ6dl8cl~uaN)h6^80?LPl8tILx5zF3)ej- z+E-uk-34tPo!$S3MQ6NIGrD9UlI->&cOO23yblfjVkh={HPdBxz7jRp3jNk!>u+v> zfY~1#+5M+A!DytX=)~(+U$g%ly{M#d{E)docFw!s<|db}W&^-JJiOBAx!kj>NICcB ze+7}>l2g;)^Gc?L+PFK) z7ynJF?x?+TmbWZuE?oG5dmaBY4~=vt7oPBW z=H#D7dGK5ckSe(u=Y0CTl-itt)3)t?z^8r;7wz9t`?;07jF(;h(@1s- zY65BbCFALvzm!mI0r+Nfq3qe;Dbe3XqGk$&rn(ygD!&C0k_0^bFaP;>mE(~)Sq)pg z;QjRXfP;Xva~TS{^Y1+UKbACP?#MxNpWI5DL%-97qd;D{?*QN5e*X7Vrz(ICEuqVL zDZiJm9*_%#sme7V3|dH>%90qg|MovcP5`sVceGc^hj!@;C}kw2-^ zNrRifiw`;L0`SILo1kZa#DzYbPTlp##e2LD07%>!Ir>cSR|K1|}uU4pu0V%0a zzCtSZuQ#1b0G@qtG^hGc<2^Wa8W@B?aV6BT=F@)+Ng!bEb;rkk{FYLPIPg2Mp_l(CHHIDVAsmgM=>5_Z|NFq&H68u_ z$GyVAJU-yz)O*h#{u*Nl5s>)Yn~J%VKg#%0#*^>KDMoyYl24=&BZe6sXHH6L&U-8}bz> zQ=9Wc_ALq81HQ`ttmp_sq2Rd=NW6P^#{Kv2fy?4f%;ju}`yveY0|TU@s;xVDOQX+& z!H)kJgXie~_tXIv$G~z#4&!C%qNxIe4_9FDhHgIn&AdTC@tmLA6>wjr&D99@-M;G3 zuVnkx{4eiWf_HJnDP5OYiL6g>M)D?m&t=XNM3eUlQix;ON(ysdchnzaX^oXsqGS}+ zr$?$7Ly&WE3cXLXPV`IMEy4k8RQJITs3`7qRhFo`#9YLidJ>MaSC{!bw-^ga5M-Kna80V_AzuU(+a zTqfAhiAO13qkdLXuLe=={-s`V58ppGF3yrK#KNENI&p8ILrTfCewMN zw)o;c0`PKC+!s5Wotfl@e6HBNHe{q0GhlbkZ4bYf|4n$R9N51c%&K0jkvv~9RlurK zi)?6__H6)7*>vaj`=&6`FKyGA2`o|eK3G-$Dv#J51>84dwT*cV`Ni@s)ImL!SV;~u z!}T}@>UyJ{2A**K`*itbl{{LWoc|PKZaL<98dDd5Ov67)y%O!FHobVb-_qW3``c-q~_l<0RT0RqlSBMOF zkcXPJc|^(u6z`QSS9C9YvG5E~mT!xV2*f4$#5PLJUAf_B!cTyiDRomBu$2 zfi-mc$at63Nxd@9jF@4cs0=9H5?*x8=wX02im#}ZFN#u*;&rZx_CUeNmV~{Q;grgs zs5W_&Ui~96DK1X4yUEARC|YZyVTgKv3#g3payylUU2WaVyT2)Ft_4x!a9dGDv3Jry zX`iH&Sa^#HX4WdFwOg-pV~&d!rbUS*r&PYgN!!$)(sD^=O)V00Dwy`MN{~1&t_D@C5|5}|{$@Wi zH59%t#h-HjA-LbxEu7MNI8^35ok3wjSsj}*1nUgfg% z`)+6T?bPlMV-h9%YS#;2KW}*Ap49I|H7y^GvKQ@vlm?NsgxgryRhN5q;^OE)_Q%na zx*teuxKU0U9k~x(0_Vl;n`nU=OC#~U(PJE@66_>=fewq(nQjvoep3n?$g%K76qqhX zs#1<`oLLPy(<-ZWde2waAUCP%vjcu; zXH0nT%~g`?TtG^F36B|irComIV{s;A;8#Eh<&Kjhf0m);ebO1PjtEy-erw|tuf+1^6UWv3)gt}DkBOkMBpIShm3Uh`S~`6Rbv%fQQh7i?Xq5y zk-2hQ%YMG%S0d63w7c<^+Q;+wQYCg+#gi#Vw4EnX!$Cx4c1pi%p)*q9L5j#rW$%GX zSBqt=cVytTur5qb-^RLF+qKOaJrhsrBbwE1O^R+k=E7`1gD2ODFnn2wBy5Pv*vPiPo$wHWxR~ zi6KZD6%%eAnK4gsgC2cnb8F{jrn9AwQtA;-e&oNrL6TsjpYQINpi zrK6-%e$;!_!@-!6f{E^4&_I_&M9S^Fo}6gN$0k3%_Ly5B3sLQLiCf-fak&?h$vF+) zw=^xE=~HWLn0D!O-4jHbon^JUv& z{1-B*&(cE91%V<6oO5(cM-`zOQmLifJ+I#uKYRq6mFV&?&T~$o6QZlOH0nZxvD>oW z6p&KsQY>A^dSq>Ok~BhQISq{b%(!NxuL?p|Gp9G=A0(=Nu{A`GtSZgz#pPB!=JBar znj>4a!`SsE(Sn%?T-LqTaw7#Ll?=Rr>dyN)%!o1XJd){$JsSWqowo-9Te)2tN4}$z z_o0)#9(2{Sy(~FoMx{K}ufMIY6F(=d8ls>YSMNPmO z@Y?2{=miItB$WqdoEj6{l5&FA0^FBK5<0g0zqC!>rz93%jYNr3$+ENMSR#h}9!MTRjbi;>`-G4r%SGl}RGO})^4a)Z>xV)EC z5`ngLN;&Uq)M)~Xa*v*MD+K=6rOj~pvG*?Rn^XtYs(~2=$U-t`nhzx!QV4>LmlZYh? z9>MzJ2D3v#2OnGdV4J232{mL%hsxH}aql0|L1NL9@@S#bR}q88n6vi+and$5EU}gl z{Z12~uE$R(2rBe6pVG!l6OTKPq*=F5YU^v!vP_X;4+3%i${R*&H#<9h({@ej5ch|V zk%0LMJxp#XEsqd#+n3NU!>8ze!|1`ZNT&|G{8x;N^MMHTq!n7oh+3xA4)%Gf?NEl>BEuPi7SyXq}SmbOI2L;2BHu+ICvXXN{>p-YZL=7oyyj;y`I zKFd=G6tLjfdNXj{GHC} zL87W`HMP#aYoSX|Rk=lhoNJde2V;{zo#@^7eR@L<>#H>D_PKn)Jy`I+johu%*5^6c z=h)lDUG$6N3e9zr`}RVWbF8~C@|O3nE% zFs#1aukvAKGna+0$c9JH^E;mY4vWgmu|KXPU z&o8VUo6aQ*tl7##eq5gRDj5c$@2CtB?@!?Ma0_n<{5CxC zdS1bdRDE)lBQLlXan+E1CFJ8$HNN2hTDHp&XGeYXc!Wfbjp3kc`AWM#3wY7bQBifUxGTdP8XFU?jEkBlLItnI-uD-m zrz%_hd>hLeYvF3MHk0$c_wvXTD)^ZQr{za$tRQpk-pwQ!Zsyu@Cp6N+6Ol>~WUqs>^HA%o082Gw7 z7H+xlnyL%>u4zq-eR(ix( z<9UTd=kC)=r4J(|`ioG`s|v;MFZ-H#bcd!#J2l&iBREs+Y9?z!Vza^-T*s$$Xpnuo zzwPaBY5hU-r}_k4KR0nmOM8411sG)p3wq!t=r=xOwf1T9L!F!5P5U~C!!~}?+wH2R zII=$x$zJcw&THY+VBY8Huou>nFJ$N0lj+@R2~Nlw)sZ)DUES?@CFK$Q4U^Cm|HcKo z<^}o9&l*%_(MWlAJ568XA6!sJ08b(@d&Z-XZlcrxamvG>Pw>L|4J_|h%tvS3aV>>F z_Fb=C8*R?mHBTNX?I*AiMe+g?*g=MGCS;5v*MBc5BXjxQ#9jG0B@Y;k%ufskZ`oB8 z&+J4*i{#}*D!RMG+0H4PHVHw0Ue7eNU`Mm5r4IyoP$|*RaFS{=gXk@HlX4v;=6Yp! zMBW?nDn3DyY~HzjJ!LG#p1(>?u(;pD>cf#Na4qRjQTnnu__#kwNR_>s^a@|7)PKjz z1F7%lc{R>6S)t2V@I=}Q9Lj&og3t6m;nmLO9eZ>wR-d!jeBG?qrtSqD7`?r~Ins=? zy%M3e_$2>p{Zdwu33JAnTPe*1Ru~4hpwQtusE0qJ=!tA4kdW{8JtVppT@hqpU@R1* zH_Y#)k-FgZmbyHA=qi|4XxMZ+}+?ChB;5&t@TM?N* zq~@OhYv9BSyJLBc{wWV_?2Vt(_ZU*qVFe}*nRcLur@pgKf(bYyt+CTr-aR``r1Bn&BX!%4v2T;z5^l zI2a2tS7D~R$p0cc!08}xuk)7d*oy76g2AnquQQIocmkg?)E23zv zmQBEih&&(BtP|XMf35N39xzzt$J4W0-p|t@u#kC}P|Ps2(?JV{OB_CSo`*8tqob}dg-N3&VMwe;@Fq6VI$1SOKr+JiIl}s6f3N4lsemkKp z==V&AXrX|7W_>e;t8)kGbCqFt@iFvYeP{|ME`5FOFTL2}6EBz;nHG~I{QKV@^#R9| z3|?AqsqCMNOich35AK_Dq5rpfH29sSdd8~cLu0WVrpAq)ZKy&Eh>bA2FwBo)`av`O zbx&WErtgO{;5f0-#Ix+4FNF~Qk zN7J_LE2qE(tlj74>ECPWprJ>J;Sw-u4wtU|^s!p)eE_#h#|(KXnR#}l)VKOqr+-+< zV~i%rv<=Ouv7s}|0)&&C$lK%mYbxsfpaecn(|ZA>hVa71XgxJS%|vfkFW^MX5kL2T zAPYF};gLQ!n77CRBAI7M=EYa`yNemarwV}2 zxg(xewxIR^5dP-a3;XgS6?|)B45vOv0ne&X_1u#L9Y)eMwH+IsQVG5XWuzQ2HN5O2pu=-s|@{{9T?d)>Yg$Uh& zbaRHHe3X{3{cn;mcrFwO(cT4kjr+YgSZTbdS;uoXT5K$TC4%fRDuAbF-u*2rBp@9B z$-V_SWndkn638FcR^v@qZnw?8|3q`%3&3WZB>Soc8Ot&auvfi}6X;qoMe}iq{G$2r zO&x14OteV8tNgWY3i6#Laqp4+Tj>dZUF7;ifncS?mi(*0K+yS`ogHDW35moTJ0?p(7CS~e+oH{rwM52qNR2{VWRwi!YW>d2sc@#L6uLknBE?o70_>B>To2mMyy z10#8*UV1Qg|d&w(-Xx95hm z-8V1xtYVzAKugTQFBP8OQn=@Uj%zKMu_od#l}VxB;>~4C2pu-Gw1M8s&@#|;;?K4G zmaJkua2ozeMl#3JjP{k_UfTrk-x@=6YMBzsoki!q!x_xLar70K<3A+;Pt@6H&H}Fl zhjIgM#e2lPT<)WZ0Ljz_Hqc6nu8|&$*!oX^FQEND3Ftc?16wj@Ea_iSwTyF{qk-Wn zx3c)8g#%-|whf?#Cp-(erPO$u(xKncK=raMJ3DNB`BTlsJ(CgqO6OfOXT$vZN7QQT;G$w_vFgm)>x;PV3JFHrn@Y=VQ3)k<{NA?IGGLVT*KD^g z8zJM7ZXqe&{3&)Sqb*?&cI^2QhQ`d)1)#g^qe`?I4Ui*YI%?1_pdfu05U@6GgI0Q@KhmTrD0q@HdmV?_Y@<|}y6`GLU& zsv`>EgvgPlt<9DM)`_P=!45`4)l)fuoL;;geepN70gV7sfmRH11y& z2d2!N>@_6@2`1nU_h&T<4HmhoC~p{*U~Jo-L|c!z7J18a*GIdA%EhxXR89u5eo0N0 zQzCTC_?KZ@Y^kJ`CjG2m-74iM2B75hx^X+h)h(X}JVszhI==4 z3Jlh3Qj(!v4?JG{B3yNU;@MnWorrjmI!qlaGm2VvPjy>Xa9?g zK3Wb6B5q|%dUR=be4b2xhNi{u@~HpR5CvERBEro5uQ4i|`6r-_mW&sZvAHFOcpwpF z2`#%BukI?KSi>MEnl4)n0QYhx143}NC*<$%$tMj4p z*zitSG##No<59OT-&6IBT3gmj1I$PwlxF&&mX>nG)QV(oRVO+9SU!ona`E zGr$LAZum3p%x)DWihe>luQDU*{{qwf|(Gy}Xa*PZ=`A=n2q8|S+l*mKCHEACD=+;&G z#BpG3W3HhsV-sW*@Tgzx0c=Zpx&RePns!$hOV1zmdZ|5NVrxuF=N7mB{7PyOZ6*;r zwJqtX0uq5gQN&m^cy0mqzWO9hVebsJZFyz>I+rB?d8Sd)DRkO5h$BHtrhzuj^>EO}GdD73t{D_I3V(*VM=gx0^jQILbz#PY#svT@|#W3Zi(rtwz z$w>Dr`xWurPjR~G#s5~hAh$QL!4odx z-h+oMSm6CRs^4X0TI6afZb9Wrwvge5djM$nICc=1v3+#Uk-WSxKataFe^N!;5^xIN zF0PEo>U?vT>(`g!a>?s=PM!Qc?4fdiHJE@lh|HALwO^WxNvufvtbaw9h_T+CZ= zdY|Y(^T)5tMya0%?W^t-&(-h3gu9Weu$o{%uQ3kHvrFOh- zW;RTa~~R zO=33*`=9&n|HlfhFYkl&TL#J4MRjH8J5Y1SaZu;>>1*B@vgvac z{%1Ff?cyzz}+i7{BM4%$Bz(j{HxAfIK@zPYG~!{{LFuLNcjKD+y7tcD&Usw z3>t)?7MsO6V@SQntSarY*dmozuX_!Tpiv+{a!6rMK3!-3lG7Owj7mbh_%BZ;t$jqB zC2WVNActm>`&}cix(YFmReYIEeE>=`*q?%%enC3%#}Aj?qGH z3qrxr{CtZWVqEDJy_*9ag+aLaPJn)d-~qpw4bxfBlcvX)>)&1cWy+c->MLaltUZqRS@5YC{Wm<$rx)X>I^{yW6b3 z%x|K^Wj2owED%Fb)6Zkmpu5^s@u~A&4O~~*6=kRMMkwQ+ANL0<*ruE^gDosff2w}1 ztZEg;&nB5-V2`=yohiWgJkqaxrLPM-9J7S=Mq{_X?u4ENk0y3|jjRoV6 zoQS_S>eA?q305LdY-8V~WqB%kMdn?kQVx2Sb7w_V9@}j3#R#^u6%gOkq8t;L*>~$R zEcwD*vkhn8yO7scl&iL$|i!Jb{j1K!Pp+$Dxo+wZT3kfm+kh|D?{ zpW;r0MZDY#S9%%6SSM8RL!!!>ztz-8`nLvUO6Ucv@H>7M$&}Wb zT9!z)6>ING(JR(rQ3{iB3ck_HcjvqN&8Y)xbE+0itJz@@EVMS5illyF%^;}YT5ae1 zi?xz9U#mwpZiZw|&O05TI90a>Of1>q--#oB1Pb>`1*uWSiv-vomC>4-4O#SO99tmD zP?9|+Ich`K$&Qm5Aus(22IjRef3C`+Top*JW0{fY>}G<5hOb}~o6?-OyVqP0^`32s zyeis3Ciu0e0QZA93R(hwkPB4D;p}dFHxaAiPK8(Woz0sQmE0KjPYF7xM}?J>Yy3+p zoREn5NF^y|72vHg)W4oRGSh$NWE*AOr90 ztJCDMMtmV-x-};Ue8`)JbI?Pr&21{#8%ibxXW~QUg81VG4#kuuEZ9qt%;SVBToHLA zL__MSL`dC=V_x^@qSqMuP?JunYtMsNtC6?V>Yt;P=E%EI&&fgLC}L0#&WI1|g+u0( zR(0gWN>^)o`wC;e@vB9CX%}%dvL8e8o-c1BColC2^@r%lnMpt@@lx#1Z1f&gx+)~~ zURF~CE$Fc+l^?$%AD8iy2JKn0lWPqh=FBD`&0Lc7ZvT(}|F@6J_a}IwsF%=IOO@HD z46>Ha>o>o)n^wRcalq9RnXoJ^jx4iYf{j||ugAj9u?@pn#J$09w5 z3)e)CmBXv!6NDYo|Lq0ve!Mdp0U{i*I!NY~rBB>USg!`H2`T*8*u_yUV!`1aO2|tl zOaCRuC7ly-Ser>5&`1exiAC=elTFwkSQ~wXqB^jc1x=7RB4=z5vU}GG&?zi|tAd!P z2wB8KgbKge&{9^1=_BGm`< z65sX(wAxp&&5#tGrr(UU`Y#mS<{KsBzv1RA3T8&b+PK=3C(49&=Vl6F&)j4-0S&!$ zJZdSeuPZ>6{+wZ-=Mo0Su6Ry);~^UQk*69RBLrH|g8b4NK99jFAC{$GX#9(5ke7RT zty#zRjpycfbjk_yID%=i6Mjf2PeP24+8YY0w6C&6Uj~grz5O4J+-{wFqJG7X*r#}1 z#UIgO(5nQk0HMr}{}ayq?PVrqBg)(lCp)^T{Hh|mUNm-@m%eEG;QyDsL{^W{EoDgF33*rxsUS;NsNJaso6;@wVM?M zH$?HqSYzP5M{O?^rr7t99)|B_8wix~Q7p&Wm$jl+O%buo%8NZRRw-$s#8l0RG;J#r ztW(gKZO}P`JZzL2*6Zes$~cm!mfiPGmZBmR1Of3o#hHjtCh$O?nh4XgiVJbD`qN_K zZzU;XDFhaD%464t?1la6`q!ns6$$;)LqWn(ybc7|{7{oH|9S|%bmM-HT@~v$96AO= z7F2w%Jed4i6}J8?WRAyk7Bl24W#)=c$#J4gQB`=YuO{1u$~ArYM~qt%kmDLkHkvdH>`GAR zI)Yy-xb(Pa!n;W)*0p@Gvi2D`61yVu`fDGp4k=Aq_}Y|@zcB5ujn*AFMll^N8b5+b z5%otGYS>fR>>Q0f1bbcUAO`6Z3-5PfN@~dnmucQjsLQ7_AEou!vX3i#6L;!{S z@V8RtI1i8SxVRDJG_ngFgvnHCl)B8ucm6CsqCgh$5x`!feFWxYQ^G-Dum`XybCyuj$>fY}zU%%Vl5l7s&js-Sl6`@?K@{zB_>L$C2Me_rG z3){MkpruSL?MeAB39;ulS1#~l4Go=oOw_w+$JKG*-T&~|?=`4Ag!yOZzFvPf^Oax$ zOS%Ay1>Kzyet%sf<`QSZ()z%~C&ptzVX#;~r03lD4t6yGP#Uw@rl8JF5OH?luf5)9 z9eu7op(*xM*2~IcDhWRgEfjMrN83BQX9^&gFsWJr$@NV^6GuF6ZQS$w#t#E3>T{c7 zuUU$izeY8JXdwymP93+`mh@c*8T6@SAQ-A#7&Pl8G7-dnJIciX>1v_SFu`F9+ zhfP^n!9){G%kZTfWr5IC!N!Dxm-rgFR)}<_9#6)}wAa2X)~q=HQeYF(AT$=swUDT* z>NHdw)H8*s-76YVo?G2!>g@lj+tN<6)(?i7Q`$VOg1s(t9lQ=gg?Nd43Infhh|{hz z+Bqd_eb}+L9>v{{nm^Q1ms-Ka;_a2agy#o~#yD7AiC|wW+;WK@rON|!HLSEevo<6%&m_wDVEA2hvx(WIjOHMa>&7HAZ zOy)M78-fkhb`G_!QFs=kTfmz(7|M0NH_ zY9zj#J(oQAp=6=F_NM{Gc-`XcrhC^jVzj~Qc9B|t&;|YA#%Qzi8@ardBbe{vrN2xZ zM!dU-AG{=LU(HT_r$SF4*}f?`SY>n%jhKMse1sAEf+uM@BVn3FvMMtu{d{Z0{YZ({ z2l3OQ(*%>*TkAu5%7UX%o6;PdNwOk1d|ejh$RjhZzQP!bKP#3Tm}`;8L}yC)*jo#hLy~V1^pN9J%!t7^NElKWKNjHKGEul)vq-E`qV6_e8s5EzhcF&sUDYg*SL51rB2+O`(gsBm(jHPR8 zl=ZR@F>&psFf2K=S-dVM4nG*O7H>OHqNW$;wi>UIqIxj1mRyj41sA``^0#NzlyFO*6N}mp zo$tN-Gaoeb%p$lEj{9o}`p-1DtOm(FBRi?7E#v#;@>NeH@0dtUBW|PXky;c-#ay1LtHk_1)k7ngaqf-x4F z&2L?wo*N}E-EW!<@0`E%*zpRgje8N)y5*gdGjX0MHr-g=V10zJHJ$8xU29jX^cb-$ zV{T74S2dUKy6$;afI2=db(8%j9=CBr6P-14*PZ;{ot@sjSw*N}uQjqaBWX7mA{LNtUsLnCj08`V8W5IwXF2ry z&MOXWne|#4R;;;#Zbyh6r#8k3lZbaVa@YLzs$comATvijPgF(9V|xkwcv4bJOr|hF zp%*VV=u)ygB)o9*n9XQyo~5_&K%DObV-b?)1vAc&HM!}YXvr2Q1A)kts?l17(aG|) z!A6{OO#)?;3|lNaMkXSv>(*XZPe|esDg?bu$5DIRO(pTFb`$YYND=BhE;FHl_(E7u zzIFz}*)B$DTfz5>R!46reOz|#{pH^^R@;ElnmKBrwseB};U%@^dCJ%rkq;Cpxt4lS z+p*sNT+?xI_B|w~mvZ=v#b=9@^;z@mr>XBlQ;+GqdJo>m*qNz?IClE<$D>S4$3l1E zipXUuQ0QsSLzibMlL^gxZQY_BJc@{gP*TKWImlU(&RMNr32rCw?r^N_xO#jI?sAFP zuo7Gk^WaykrN4|ETW#yc!*rR{njEJTVJN;Jfmd@ULG&SSfKVQ1sW1mt@O$0CArzul z$i4A;;{A@`Hw4WQMdQ6U%XJ$gq0#^BeFgs1FC0vDG4E(Fd=#&D+HA}w&uWuLvi9LV z=by{R+7m9M>{NYm=c**8x{sPAEq;w-H@EVLNt@Es-A znwsFOV>iGd&3XSvF8*lX*}bE?ZdR7C=15t&@|v4;6+1(WJFKej_e|z4!CjXg%Ja+Q zYTxba`RC%m%HWklEG(y<-}h0T-9tI3=`I5$+!>D-wri4VBefZ`?}{Lt>YuW-3cAm< z(c!*d2fCmGq`YDb25U>QIcKUMiwsT z)GL+0%_Gz?R}Jl|wYoA$_d=lqlcfH^ug;L6}2|m%kLaPYba! zIcaDP{DP0k6E8<=LI#k#5WPpq>j^)dzA{PZe)WjQqNbnWc5@lZ@jH_0TsBwox1*W+ zU4>gZ@`G@axopPvN_#GN`TZhc%ntxo%f?{Ory&lq7N7=piB<2ebf3LxIbHZ;bQ@gg znZrtg>QH{84}am|r_9Rn>9fzq*yQB;efYm%f=;RoXO)qp0MV_lQeUv3U2ozZ`DZQfEgLe5_4jMx2O z?Pw-U+4XX?x$p(T5v%r82XDW9QUK6_ddJ$Pw6VG zdk>TlxZTL(l!iH}msT%#Q?I9x;w|>L!fjmY^LJUT);BxH>v|+U95+l*ZFd+_a`%a= zvF}sI1RUyuNa5Zb_+SaOu`$Z3WL2^%x)FV&VNLSF^2_Tw!Qr2mwxh&G`qSTU65)pr zbnTUhSB({MUf+;%_#sj(%zO+YZh7XWhs=d~MdhxMG)Yd`~G= zJ@%Z0?fJa9ugcb5XNkmrCA@OSB*cck>!^ni{cKwQA?KakfAzdd-4_NcY*ml<>3$SBX+FE`?-$6 z;We>emA-~?CyVk+_{(d4DKM*PANK04#$AS~rTM9=Y}42y>V8dg&)wQI)?H6ku&=+r zu6HhnCzKxHm|h+pvbAZ^FW1#fs)wBQ8%WrB^$G2d`Q1N3xY;mv!O*GKeD^P;&Acyd z$3Q9$!B-S69wIHEQ*;_$C_!1>9v;6KdP_y}ozojMW$j7h`ioT!Q=XF@uW*7x`Yf3B z@*bk6Sk&EFRXe^03RiEWCf@ash?&OD+O#X$QpV$ZEr#ZQD6M_oto9{olwDj4DhubX zPCn{T*LAZ|Gh7&+y5Aw0Kl)I(r1e81>T17W3%tA8;L^VNFGiUhkoP=6?^VydR&Cq} zyz+rtzOD!wo8;+|+*Q9oAvNb2`DHo$I#+D7jl0=nzPM3&m1{>xUo2;k_a$dHuUk$J zkVi_6hA!zuUAT-LYK9w)4rzkR-pD13Np9tVw+{NAO1K6-6E$vEIgitU{r4 ziG7#H@LpCB?VEPo4A*&hb!{lt(o|{=Z%(=vZPX_`naCLw2q5%XVr=L9G8W5VKI&DE&$ zKdDsT$J&{bOZ4z=*1<>(8xM6$jjBAs7gdC$KeP8wl6}~dXNN?V+3bDzM=v~T|W zWm28{#~&w0cyj0BReN&C9o(77JQ)dXud`|^U-PgfVGGwbzn^@d%l{C0Yt@7FMUYA5 z*vaOLKZNhJE7rAQ<(%WWRy4YCmMS%;Vgj2m-P*hu0p&~XFzAwmXF1!!uc8;5M{7dL zO1M{l8c5Wm^wh>Udhgc%ATPu1vaSsMX!6#wLV%IjGrf^@a>(HLzcs6#3wCogx5axVc0XH2U ztoLb0fxrakm2z;uKdF?+a%}feooR=lNr9b0Mn9HhAMvlYE?az7J+t}pbA{I6ySXFV z2;Z0T%Fb1Ge^tGzXPGW#)ZXl29&F`m-E2_ay+;!}sv1~0@hLJZD_Xhhv4~kSGDOiZ zfhnz5c}WJz)@hDjvJH1R7CFw|-i5%!0*S(#*e9O))j4+B>)5oesA)8(&SUVkk5}XSz*}P{OM8-DSw$;aO<8a>J% z5vo(9Bc}|!utD)mI3ofgy&yL{&jxY(rutLtW=Fi&6su!F1&*)pbHow0exv#lrSi=k z*x|~|P1Vl!_o>7ovu^(0f#|5o;}ACNg%}liEE1vZGc`*L-xT;lhP#hPLHtH|2=A8& zN9I(N76i!f@TaA7u|osox4wF##-@2r{cqZGv=${uuZOPVvZB`5nuB*83uP_i?`-JM zGEszDgA_ZRBsu913+4vAsLwt2hd!iCPnAm&q^6#Pcuy~fITq;sRK#f3#9CFE)wbUw z!?{*AGlJ~Lj|7uBeM<*D>dwP%dr z$XWj|d|_lcB~+l6N$&YPvTuX+r)A!Ho2;|kR~Bdxwsl_pb^ZWLtg;;{Zg56KXuIqs zK`)N?b$fR2ADJ`VlK^x4`iNUu>FC|(O@3!1H{p2sA1v%%H{QZpeiR^)ns(Hy@?%RdyP}s0W&ASzo z<%Bt-0Q;TW(`1?Nnufxd%<;sp#H>J)JR0S}1Y?zQ=DwI%l8=#56bdErZoVr$!aeor zX#bepUNNSwvzn4?EBwm6OUuL|%lStQYZ2C!+m1}gkxDDt#!S13v+gA<{V%*-eqygo ziKq?w^t>2^5UjaY&3|nYjZV~;2nZi|IkJ$BjDb|7cm2Z?o4zlN^#4TO}x%hSoX&t5H*BO3HM z&y|gJ>>y|?Xln59XjdNeW`pcG_Rr^Z_VA4SkCS#kDjf*B z_+*-7wv+9@4^e9ZS1JmR*F!$Eryf@hY?GUokua<;8E3VgAHAgSa56@4w=ko(OSJ$Wu9nde#X5-lE&Tit0XcD>BM z_oki&w%G7n*?Vey#N=a)s*N%QO~u`iS^v;xq4GlLW5=m;E!Re2C;B~+9|@whF5$WX z;ik(gm%M9@BHD)+mLv@_qyC|8<+-p6T7AN;l7XYj=mXMMn?|m8?#^@ z4hMTp$?->YOMMI4i{Ys4c*VLDb~8%#OTBmL9wi>;Gi;t?oR^=zmCYS7$}(^?c^Nn> zxWZEB(CsMalW;UQHyKuu=5W}_{sWU zJ$-->Ei54(!c(0?yk-T30Fp9ZSg zi}x0P%yXA;jyI!qJSR4UWfd+XIZuxvq$^D|D2Absv)C?M>iO)jp zl5Mk^)$@{7IkWVjz39}}1d0Aa2bY`Yx(XbHV@0S%nHKn>eLHV)&*;ncJePZK`jDEs z^v^N<&-L};Rc}`#IsGRF?j=!vK5g7ly3I^c;ckM%GkaLL5UbaT`sU*Ym4+gwd&^AS z%EG7$oF~jQZYVCNZnmBh<-gCGUln_xmrKP1?8l)7EZjg}fp>-(90g0h&B!?b`0fzV< zyzjl=jrYIStXVEx`tY1{_Bnh1VxK#tWS(k7+w=V&198B^CY~aY7GjVmz5D*<8V6?k zi^r`*XH{BNc~HMO5b9+c5zL#|Jn|vB0v=_v_0z?S{xOF8iS?6&jPbq?_Ya%)c$oXz?R`vEzsMlPSbez9={ zC2ez-k*M}V)bK(jV5KBkg_2Vbqw1j~qxhVFoYeB+fmE=W1+t=O(Vmq2jz&V4>n!2I z>To@5-Z$~pR;xb?k3UCD@hA$7q+~nL|g2f8l)@*v4`8-mN1C~s56P4wd zA?eIq4Ji~Aoll@{8V4qX#nxjR2i6uY2!IyRViR6qP1#QP3zV z#&ocECroC;ih;9yj1WsElZ(|a#9vWVA*I9gWy2^CsGD`?W1s?-3UR-<=UDF(f97{S z+OjngF$%})ZDtm(a4MgEh8NomSAN}j8*;TwXTlLB2({hxvN_Y;JT1n^;xC)7$JQ;` z->k%++7^2;q=fj9u_L)nS+OiB?dNL@rNuJyV+nDiL;8d=|GEu1j|z8wN#nnKCc*iQ zNu?p=ocP{@gv5tk*GVAMNHr2I&l3EV&QeWtN_&|To$ zW+A`(>C*Dpyz2;~JkbXn(!s?*1;Qkwo|Vdt<0luBbCVyVWS3#|q=Z&Xe5DdlbAGED+I;MepO$H+KRV-zfZ& z;rgbQ-~x6tV07s+eYRj}nptYDe?g0qAkB^^3+Ob)!_1gqtRQG zNN}vJhZ_Bo+qMjA9?`sFb9s#y&x=E!8IR#6#G+WX@0rEew1VPC0YCM=eU7R2)d|W} zOKpQC_wL37`ER52^>zCpN{W=_jIuYy?YWjgTm|BJ4NKEE$sEt)i^b<|KDZ2-Pf*^X zo5#C{UP%=OZu=z~)m7eEYWd}TM)_=Zv{U<;p`hM*GXH48Z~gTlY5F4n$B}zgtXxh& z=O-HE1|M(M_9e4H6$8b3YV00@JxK*6Z!k|<#qotO;KhocrQFsJP18MZjYak5%aP8I zO_q&@@EoGEvw1C;Dk#P`wa_WEjFrQH9p2lf{0A~n%E`fd2_9}gDHt^5mv7E!AYl*~Y} zxuh}>PDBsZH09fccnfHXxRvDZ(7t##nGU(Ldj9o{Q^&@;3zsDK;5s@itjC0y@8dzP z8D=K5JEJ7)`4ZngKNxnqxomZiF>1uZbok*;hPIJU@Ap~?=HGC=47e%43H%5dGCl$~ zId|FT_Pu~}2`hHE(WEZL^ z45_CmxY!IG!h}Cb0UnBUu(|TM#2a1}(2rlbPZf ztosmT|1x%OW?0ds{6=h;cK}u~MTLcrCm2FMOX)!ReBzFf2cp8_%TaBxcSB| zBvx&QMx2*^yW}>fpLB>}ttqO?|M-sA+H)R3(h9eH+i(V!p)qJ1Tc7-;f~0!qC4Z#@3o)w>$^t)eQUwk55MdPay(KGFj|>JXaFZ19!vvj7SN9J&9L&H zLojWFCP^7c^nfNuko>+4TP7d@%f#53;S(kB?p%9yQ-RT&#;9F`lChC7P71Zd zFyc>VFIv@5?gBe|u8#&f{oRB}1FYs%vIpK1%;z#a4@ul19g<(bN4bz?j_&WWJgD@= z-?YN-Vd=TRK`MIni2x-o2b7D~TRj=`80HqvM|c*~%$_hQR&SeSc{auDU~)->4dOkU z)$}p2mCXI>`kT`*>U*CXN>U`*1se+$n?!%V)QX4&nxST@rXD=4=I?e( z^9GfX-kS{piqM4sUKoqPugLynJ7gaD$65FG>7kxej<|OPxMY04bq=FO&hx`uk7#Ht z#QaA&BC1Eb@ii-v>b+uF6u9FP_tSu<}#3P?ip~@q4C3Vb){=u79 zC}mj@VoR^3P9utPHV1wCEWMpJ&K&qzEd$|jp6D|#n)cq{qO{kAfL8daBk!0g`dNR| zEWiJ9;5|!Rvp5C06rlIxF~czc9j3&Uc@7^n-Uywq$M+R!DthntibL~dX((Cu6op` zAd4rw@2upaaKaIjxA2DFfF7E5*oE5e%qLd}62!tqb}Zn;)0LsNb49#uhYgM5d45a+ zg9xqjI3EASw zFq>*FK+FEJk{_?7I351-CCMo>vFOTIbIZ-AGQ*;?2S;~Yu)Ho5VV8C4Tp3)2(8155 za-=m|2}@-dmh<3A@X-zF%DD&z4c@kA9|&oauEC!~tUINk^9zEFOXT=s_-0Ppsf4TA z=!LBuxgJ~M?@ZN_IKgSM)FWnYA*B@s{CEcV>lG_0*e%+wDCKB|vY$Y>ldM>?4({b? z_uPV+>9V*=W}&EVJ$q1o65jX|h!ghW0kQ`=2?Mr1a9OjqtG3$%=M<>elE$ImSt@$# z8$P6&Cwg@+VuYAZ?~+xxfgSDAV>=gxGX#6c6wE(RlVO^E*ZN&0x1I*T$JHMm&?j-@ zN3yZ3=g#6*5zvoe=0$b2iq3ny3e9llZHSk+yvPj6j+K3QP{LBmTzU#Qciy6LP>As6aM z5QiX(lZJeZr&nouPfOE%;CwOAY@8V`0J+gwLk`?vXc2u8#s9(76jFcrp({m|L_x3~ z=;9nMny z`81DKaQog%9Nkcan_o1qi;_NzY5*JYG@6wbky)+KsfYW=-qnuswPEHo#9b;Va|K3g zj?DUXkTMhnY=>uQk>4ouXinX->U-mlA1K54c-Co9rMHc*bow+fn=a!<8dRnCPGiZgj~OP{1D(gR{g&OJQ3PJ3j-D!3Z`m&}N|fz%X7BsKD>WTNKPp0PGtk(x z&iVEOFWO$NMrZ{Vlh4E_J)}|6=E~T>wzgsV;`1^t-CHg$sZgt4b1s+m$(~uP_!^0$ z?{;yRT%YZn&zs~nX}T&k7P2^7?%lErugsEnz4)rI4_v1`Q^66U%uq!Mq zJ&dBlR%gr>hbnrMp%(~{ZQ2H&&9+ZR)TF4g7?)w^#z)-CphxOgryFNdvsP&lMVHMo zj4wQr#+lQqRq153fBk}`az!Dpk&lfd6jE1uEHCVZOjbcfj}YiIx0aD>*vs;(3vBQ2OZyxj&5y810yjXBLX>(cx#6`1F z--L%w1DES1tw^!ONbYmm1xsS4WA2#Nc1dP>f|*$Ts651qtdYFeMU37 zsQ!~H)b=iH;Tf%25X6l@X)fe1j;8qpqz;8+r?sHl_8uMTj$&41+@Rjs$xGy!%qm!;KKI4flc2u?4ja}3)}m{(KVvI z39*lq6vMR%o}+br>{n)DSaMiRztYA#m))MkSS6tn{jNHp1`V;2XC#8ouRTOEIhSc7 zmCa1XPc2vs5Y;rx^H{vutc7iQlT*036J@=sbO3%(upk4{K!;Miekk<50bNB=(Os(* zKG4}V-4)C7(Rr};iKn2)JJ?S$q-$s0^PIlvhk%83k6cf0TFWZ;FL(G^0BT! zpbC#%6&(<_91o#rb7q|EsPTDD)fZ1vanSk<>k)|jJBk0*r1*OJL-&oluGSxHvYPHy z)F&Ii)^Q}Q%G-LFtPJpU4i>)-SO6&C&tm^g9f*wap1d1paGrF19$1YuvH2DD2`)-c zwn}O7gdkY|zyMQjWsB^tC)R$k0+Lr*q=mm6TEM#WcNJ2o(smTXXUB&WB$F*L>38Y`Bc9);6}#L`w0=|*bEVw@U1`E1 zby;lV^fHB5_#8Y46~V~TCQ-s~c3!vme{1Hmp6*oMTbrat_)y~ygjeFSLKIBKW`H;t z9JQfctaIj8ZqJV;E%N^bv#)6HjIa8B%Jf>gC#4rdN`QXS{>apn-V!b`R^1_?%}0-F z`;0)Rvi?;*AUhP{bfyI4MqSK|m%rd3pu4!&KT2|@K-ENmX#9O!eHkR#$qGSzFe)#@ z`L4XnWXk;d+0%XnuoS8wpRlJs=(LTY&V+N}XsW`oaqUiw5+ANS$+P#SGYR7T;*Dow z?5TlmQL<#6r8vu6&bymjK(>Q<=4Y}MrWj2vX*zSUNwiuXhty!0P$E@SIwqpv%k8ax zu}&Q_52sTvdK6=FGu%C^ib|W5?Sb)#dkq&JC;dB7>uv4%k&G7rG4*vU=GS|*lWxx? zsyL!G;$><#)vYiZZr`R5fpaX!bE_m(^zpkH%N&`X`>qsj#v2&7m2#KC=oSiWwjx?% zbB-Xpy=A6l8k~tt8xrbeU-2vk36I(*1VdH6G8$9PJ7QF<25_ooI%XOJm-(1s%@?uR zbzGxL3%-7GVp|46-aj0wIv;a2ye&!bOwak@gxMJo-#ZXRGaSRWClPqY=}^25ad$5!Hp~IOW?DZHlACe4P)^4 zW>VUP*(@4fqrLb(JOjdlMMOe4T0D0x{3Cb1AN5%o_kSBPCLjw!kcM&a`0VJ8x-}fd zeJ&Tj6>z?ptTxvp)m+QFX4;%}ES=w3@o1Z}o(K`Fl69(6RLR>`zAI6jt=&{1Uem^& zFfJCc@WiKw#G~2MQMAb2HrV572WrnNrbHKI-l!A)S^6}DWr$l&ylFK;z}bCAtGb=N zS}xwV1uN`4Qd9a%mxs7VRV%ZhJ=Jez#GmOs|DaS(F4W8b?1d&k zLvo0~ef}AG#Mm7_#*SLIikhALq|3SQedcqYZ2AQAs?Sr+vCT8 zxn9)-n-WfFKBz4UpzDXKMm^rXDSl}BkWk4~b|~gzCdCDM-YD+Q6N=cJM80X&V}8!! z%S!|8TH1`}Pkeap$_7CJEU_VnUqL9tX|lmYzD_V*f8JNZCSvG&&vC_6hw*NmG2rKI zI#~1-%b6bMMKT*QI^%x1d+b7N6CHW!v>1)`-03fFhL=?Yx18#m?dQeJeTc*HV8!?^ z1I}GA0pL!|fX=$II{X|kw8eZrQ=Z)1pPPYmsepU@n@us-!Y)dojg83rK<%-Z9N(LB zX7Vz7@(#MS!F55jru?|SwwlhlC%Rx5v_34p%UZhRK1Tl!q$6yp-2Sk1srLYLP1aG|@iH#*cAHg>+}IOs zWAoiQ)A!82-;XY`BZ08|6TVr14r`#!apFdT+ISF{+?*pHe&v?p=iw=~>cvoJKT`7x zScdxq{XV~}(umsV#+xA0JRS|XkOT=N+z3Yl2KEOf-WPUJJMwXAk#;(!pGopc{ z?s{$>XZlVjhGLO-22+#IvYTp_PYYuVaXMS3Lxs&xl1tm&kSF=so51ZPVLp3DS~GZ8 zjlThn*W9P4-n5dL|2h%xS3EL6AcW_ILJ_VZ;H-b9b9~q79KMGp8NhL)`b+l;ZZ~>P zkFEU(&vKf5Xj447Fodi4@y`BUz{x1np#D0dVn2kNj^jDF$2-Cp*B0HQ&zO>5R_wcK zv94R$`XCo)-7Sm0(i?@AeyeF^$^g?T=}a_bU?6+J=r`=eMfci*Q4{vDZRM2NXqqH= zUxQ$DYXWvGbpB-HsZ6*Bw(b%|-x9a9S5m}QX2s*&A&(oT zmn{Dj0;l&F%q{fkEhT7u*L9v}8Gpw-cs#{^iNW=-SDKQrONewL zB)yA{!u8@9n|5L3=hf$h9UdFW)`h$iKD)8YuJd|By-YDPF!u2SP#(jNDy+)(%L;n3 zws@PlC5gSPFV2}`^OQ3P8pu(bZ~$-nVFPlmQWtff^OEkPoH)`)-`wSTZxOxG?>T87 z+;&HAydyZ=abJM9zdNjBB{Z(t@w5HUXDm|v#3+i^xNaBuC|+QYT4yL1#QV2vkVLyo zQ~^}<)Fw?F1E;liKXqqqln5A*`x&q`$GXnur2WeVls(-T;dJqOQn}}X#O3&lT_cER zKFPD+XLvL|k^p6QZW_HSWti?|5n=HYM}D)i!^l3_akrIfAbIglsYGzm{g7j(yYfmyT1p^mPMe6L_dI394;4*lX3V@nWo^d8E>CAQo2#yDVI;*oko&}hS~-H!9k zmW1tnicP!o2!}`L$6Y61Jxa+z-Jq!1XKM&`Ru(DE}(Y1dNjVbpEB4>dg7jX;0^Z1`dJQEC{b(S7K_ z!To4vq3=GZpPj@)*D56B>n~=QX9J>8v+Z;s(^v<6=g+G!3&2r}NI^?g4Ys0&!`-LC z+6=m=Lxw{GNrqsav${q(J<{oQC>+6{wnzUjkr zNVA6Uobc{v6@pAn#^x)EN+QL1!o7hwsnhIfzedBMgIyx3V^s^I8%4_HczsA5t(t@K1&r2b1tm| zEAu#eG*&aVSbH(bNbb{vtF7}8$)YNCfHmJVgHt){Y}>Xi>;a-KACV))wqJ|Th@c$t z=uIn7Y&dFTJ81a$BuTNUUxaI+epsyIiwGTl191(Y8T?mHvJBdoA=C#X$k7DJf2ohZ z1Fdx+Zili9;~@xe*O89y1FpBm+(8?45LqGZYe-SwsO#2tFtKiGZ!fY^Yed$}Ezv3F zj)TqiWN)|7FDh4Ld3S3eAVm|(QKudoLk{j2JwpCO>{koUhi~BZ(mS;M1{Dcy47eXg z?Ev@$JVupkAlSeD=e;_H0lqu#R)}Y@$hdbQ(P!V~K|0cID!h)ZJY4t!LhtIVrKcjP zbSkC$YsoA2vV?!WZXnE+2_yM(;aw*bt_xk@h@$;SYEvMd71A}H`4|#Qs6K~iiNU>} zc#dH;o!I!5Hkq(nlgNtl3+mAfN$v7WcmX8pz@V{9XsEb-@ToNnS!jCq!zvTOHDMt0^)W>mHI< ze5Q~{?;%9IJ=SMD)GA^WifJy_tQ_smBmLO`B-r_aeeq3ZniW2Vi%V0bU`e&9kZ6KMHL?_Gkq7P4B(#?Sii;R-J-1}sKm8fbuZRGdS&c^`)Ub$)F;^< zSdUAl0qN4Pz`pmhixAh+>5<=_VK$w@jZ?$4>dN-@rDw6R*3N{P_8M1_r}^rA?=vVLC`aa+(SV9i?alCD zp6Z`tV=pC^{l0p&$aJ@tzt{C(ZMv=qMAyAMh|v#XPN4KKBtS)hW1V%@DJ!Z87KxK* ziL5mCdZvhiL_s&B9`-dBEMLXwOPh8W1cWmWspxK4+eDm?d+f8a!l~O?b{m&ZLd#>1 zac z%&`yrZJPu5%MSjk2#35ub1TBh0?;e@TK{OdSJ;-uN6cHdOP}u97F$igxU!%4bYmU= zU8ZS;gF$cTie$OBOPM+o zIslZM*Ycx1kU_j#$No&Ja6=>IFM9kR=v_<_m1i}C#G5qB-~T45g|y4DTow*^R&*#E zfSg#DLPr|)@ilWHYLDpeG2Qx(%-3J69+s17h&xm5{}vVV)M@ngdckk343fu9S0g2N z;Nr4N=r}k{(M{d(z;>JQo#7~zY_I}bn3M{=GcstJ0lWRVpSJiT_&7G4XOL!|S z@yOXeuFA}uC9$>xiPu%F>%zeAF;xw7gI8|T9!1?rNXMxYAum_@+9u1-CIp(?;17b& zQCd$?TT5at7P5Xq--%Biz8t`ZxTRRUC$@D|@`OFeC|Y#w6geajUSmEER`l=K?XEQ5 zZv5qqx4>uq?zzYqQLpv^@YR9p?V;7EH0&`YnDL6Bree43M!(eHS!tJw+ z^1bw+Dc|KMdF5F=%ik4&_9S*pcMeU(^L*cSBYk##z&fG^0ckT>LO);3b@Kb zZe`uu#OC@zx|fctE7|3JC*R>fF(jf>_AqltkP%bVLR6)ifuN$d-}jX=xsq%ndNDGY*PZ)P}m>u-k0_a zc=491=2j-ycd2ioQQ?w5?)S?x-bm~;$C%lq1+@&=| z#O*n365!eoK}RhvwMBs#!>arF3tc~bDRkmC9UjfC^XaG-&Xc#I{bJ4@C)R$G zmm^cqp8-6GSXQ)oJP0SV#Id>)v6L>~?)-K52l9jk`gPaA%Wx>HM5p!_gf``FT<#OF3%WP41Gd>^A0-#ex@3~Yowz8HCSrEI}NjE zJfDiXNjr_bsCW8K`eHxy)i)8B>ADG&H813a8Whi3UB zzF`U!xXvJsL&1;mVAGoYJ6X7SzYOWLE1_fX1UKe`?#2_`WfW&(p0$EgFA@C%>xjdX9z&xrO2c%6Zn88dc$r2sP&rdTF}#;QgA1_Fw&YmzpbHc{$R` zSm037cQcCJc+0S$YUIC@nHX#RaEMuZb3NWF-il^dKg3_*rQJ0-@SE1gdZ6%+FvU)10~wJv38{D8?eSvtx3&BbR+`0hH;K28(* z22Ucd>8%&PdCB~bfAFvGK#7M=*@OcvP@^vfyJrMxzk5odV`Xcs`Vm^Ue~?es4Sj#= zxS2s05ec~OekVTtHlG-0lwd> zKMl|I+lfJ|;EOjn>psCfBH1#noQWOBl~e};go6M;{P6QL;@9E<-FpxKyBxq5{-Qiz zj{Hdhd>=rDkWOjr#px->!+kBf zOCV$8AI3L5cr<#J`^P=_vzg4xo|X`(KR&&&G<`+FzQZe<+Xs z^ZPIYpcKqQWtKtI(z~jEXyd21ZmR;jj|?TFp!#1w_-l0kK`Z}7E-PM>%bW!Ns~W^_ zK1uQV?@|BP@G}KUJh)yJrvJMt0K;bQ;9oV=KZo)Ezay})NUuSkyTSiE3qd?mmjAPi z{WyiI11<$u0urCIKf-rB!dn3&26 zQ@-$CPa9JM^=!9UJwuMoxLI!ILsMIJ4Gq=~P`kedGrWI|u3ZpI3)1^U3w2yvDyB-W z`-=U*V`)h>i<9cID0;Csy4Gi9G`*oK{yXPlFlV8BNK4<|rbjKyof5g(5_I|ZLwd3k?Hp6vy!mAq_@;x7)y;9Rj4gZ}g_2)6TnXe#O%sd(YRJC@-OP zdmss`P0RR~!mz$6Q^P?371*U;16s8`S@gxU>AsUxwSwwyaq~uzX$wm%*4a2mdc2D( z_Z@Xbh)<@g3yiKdbE*KMjI<}x74xk5BwbC6&-KBv2}QjW%+1}xMx)tw;&(4B#0f^< zE9Q)Z*t#mE2_h|Pr@cqdL}sa16QJ!v(q*T83N}<0{->zMxgVy_|09TAKK-*faQz>f z!wV)_nVP!qtFmB+xtt^$tF~#A(8mT*OK+4QC}Dj^3kG9}?v~dxZq$}p0SAPR?PcnA z9T(Dr!RTnd)d5Gd&iyN!w0inP!M(meoL!uRbpm+>FrQz!4SM^?Kms{(SOn->yLfsI zc=ywsBoBLt97J!vJQ-2Q1Gnn#1#KIAnSEvc?mvEG69QntYTx|7R_)8lP(=HDS~}7; zW39jWWhlZbd2vnR@UAjAtmuhEg=xC1wUb9)yEHD)RY0mGUEN-EPx|s?tWg*iP?E=@c)NIcA)QRIKy5PyAS&^x4g7;klV6Bihd*opAc{1YPj}PI8f5Kb%ZwKmY*6H1rU2`7MJpz~Ue7Xub02SWkT^A%{zMAH;TKXlc8GShl^7BCcIgHC6+6J7taqwMIEvQP7C!>K~j zY|~*PZ-}O5ukqlu**$;0E5AK588D-QnOvdZCx8PC!E|gYjv+ek^ixtUlGkWN;%A4Hggs7*RklgHr9jE1n9( z^gNf(wwIv$p`G%AUG+0D7gPIzqmAI9z%OXw*^B(v)AOh|Yu8e%#s&VxSsv@Rm69H{ zMoUFB(H${J-CE1?(>+Xf3b}gw(kdRN$)(&Yt!e9UhKD*NzU^W+)F{64ast{JFN0_(@L;+Z7(-G`|yO$=ZLxR z^o`pPP+ABDT%j6I_Yp_l7!$^tu%Xd-Ru-{kP|uoy?bh#Pz=6gw4ZVOO=>aZI`j)**1GAjPDn(iy?HYmr721iTiP+ zC9e+yl_EM{y`7J@SuJ*)6Dcn*7MIx7(=ZoZ1Dr@IJjhiI)IIE*l5;o|*|; z?e#B)T7IP#XMnj))T;A^&mKZhN8Q?sQzXZXO&|n@(cjgk_aecDo&&|T8%`U(xYpw= z1=zyYBrq!Cmnm{X!VMK?OK|8=a1=wM+j3Oy^iw|~;fSppm!%!X@L|&#dgNeAx_FDl z)M>K=%ddsXOK;``E4v10(U8dE+`QmVvq~P-p(K5~<*bg2aJB9SPzIw*JGEU6UKzE2 zPryDsz4<54toqwLlXGIwtZY@7oA(JWBb_-(*T_hPY8PchKNX7yb)BjI{Tb#V$nvg?$&E0N{7^>A$9<0l}E34eiZQnUguE?%8< zW@xoFr5|76A>)zwSc1EHCVKim=x${!GKDl)i~OXgg!KIKf}f|~Pp|Z|!9k)#N{4>n z?Aq?8%q^CO=N$b|%+_^W9(dJ&Q~J(r7Hsjy1!uS9mQ`fXALhPR`ere;R1Z}f^xT=b z*anWTGEmA(Ac0eWyq54GcHH;?AP?*Fqkbk_pj*SpY{9E;!$EWLOMZwF&NQ*DzW}{= zw18tP%D;M|saehZeDy8POMIgm$fF7vqSD@)X0md5a%rZcX4z{^l+(lpxk#x>Gr-3XV%1jjq$fUr| z+A7OHV8X6`BFW((a$QJS*RUEg%#(QLFP!N4>AKojJKJTk4-amf+X9jjE_c;4w49{V zq!$d_4fb#vHig~7emPgPVf{HqrjBmBr5+~sBm2%)g7Vlp&0OtFO*!#3Ml^cCv>Ic! zDC`p0ROaut{yV_&tr$Vuu z?YOPga7`xt$S7MegS^gV*iATVe(T8caw1u5{O?!(r2YQC!7T8K-QM%Fvv3rk;4kb(RcHu2?5-n6fZ*$}U@dBXNHp%j}{^ z%dY*K-@>_dwz$xLWS>*_0b7Nd*4E!<`d{?c6bW}y9#B2yIt4~5lYRdf5o%^`IeyZj zDX~_dL5~=z;*q^AQO-~7XvL4~PV%Fg;aa>`i;2tO!xDm65sDeYAPX$j^$A^vxUA*!X1s(J5 ztD4vZ5xy|@);>I`0ZJ9uTVcB#OK9Qt9GTv?kS7wa(DNj$i^d!F|{DjS(Ibda2V%XGtWMG+Rb1RdX-q}@~D#-iV;3Rmio%XaEU(0q)FtWe<5Vqh6 zr8CXfT%!5#GD=OrGtABrAzO`E<90Ec6^L65MaRB}@fT|F0G*MVPS0j1EFBTmt?Rqw zRa3g@Y9gtWMm$?w%me2g)02UE;7OYmsmmUz4tLbN)jTFf^`N!q^17)>i#C0BWrv`x zz4ooVZVW53@woKre#SF9@oq?dzk!} z^x+?6Z9nL`7Ji=cceQZ)s{x5F3D0rnf&)OfMj-p)BWh!+E38U#VCd3XjzYUWX5y{? z!RqF^u?urJv(pcf;j^UNOmmm9c7&4%kpRV0CBtg#8ukY=RCYGEMIzd(?CnpcQ#H@T zf80k}*Qv7`EK20Gi6QGAXb`V#<(M|U)wWXFqd_R2AKLB}gtQC;i$2fa^adfu4@dPm zUUg}=N`_n}>aD9rMfB%`5B!Y6J#L?Jf}FOH6jEy;WFZ|2)~GjHtbrSz(4!w(?}UY2 z_wETg^!pi!YKLIxz2ITPSoJ&(dypph)P7l}+iXl0U(MXdFv&00xKM+Tq_7Y3TW8Eh zX?m|?Y2e;xCsJAanBf-2^rRge@!Wg8VK!>}HEr!apFIMU4cJAqhS33K-xq)iS104` z%)o1{B~Yd$nv~M}a$k;^??sX%yh3-G2lC5+HpwWwPXSOwYL$^|P?&xLh*p-qLR=VF zEo_Elmcuy3SgEI`s0_R?{Sg6PW9rHq)DY$i>F&t0FczUlv8#Z?Ho&fzp6tSTDj1@y zZMUNjFo_2w5krn_KDPNp-`r2SUkv$QOZV=d zrF#wD{Z|j2AfEPDLlUiI&&)?~!Xi*zotlzRPWo3mQM^Lp*s0s%fX2oY^Ef-nO zEP?JQbBFFQAwFCLA?9@5cyPKtD&&tofeuq*F_wuoq65^ZN0Sel)W)PU<%@qoZXDvs zP~NIp6bcS~ktKxXxWH6w#bOX}I?-6@&`C&t>%2Gk(-CH`!zc5`k+-2GNg?yg&O-GK zM9-IQA^CRB%FXb;Its=6ouZv2_NF8cp!J!ILD+p3BrnQ6*t%5kW{vK_@D6j4^nkhS z#3Wjd!5q+Ex@N(3*mHX>W@X0xHZDhv)^B(&z9_ag-PTnY5>2(xN-!Y20(}4UD*G7ik5*`&lA?o5At;cm`m3T8e5GdgRHc3V&V43`*!Im*RfFHw;! zGDAA+Zd4*f%R1Zj>UspG=32tM7=EYTg{2y?H~C9xfO#IvT|WE|AdCbkTDL)e3!d>h z3Uq9=>$mca5vCpS;c63(z14@co4t}qeTW2|*D;8ofbJE8GH;HZ(`{t*j}sP)7@HtF&S6l;m#7;rfm!_pI|ZKH=>rZ!9UX-aJkzV{rBpwVy{U+2J7&j$VIeMEIZl(i@^Vj6T_5~p*e6KP9*9Lqo_|Gcl)avy4MxzDiD zjs!WnWtzMdvnV#N@TXQpuHK}~WBM9FWbYSk4@kokR-2I_(zMON(Z;^xur~(Fpt0t$ z>}*%$KW6v$=(@_D44EcTOE}^B#RqZ=(}*$%ez!#!EZHw<{DLM-2dzgQ2 zjf3Wyj>PPyQpATYkV!c96t=oL^u?iwoLuftqOBF$GuAin>B@lKoBqUi3VZfUBBI%S zZn#}~bL+Mp!*TQw3K><%`G_6%W=Pgu7=6bjPDaRb>9MQq z8^ZX*h|5~zrUN=8RiJ9x;s>L=HEbospLqQY0ig-J@Jp=XoGZ}Kz$9vzwyH`^0=*zL zjz1ckdxMYl14Y4DW_TG|vHa8Orhop{(`{NsaF4r;+c7RJ=}q))0@&WQFTCz5u6_3+ zli_^!t(0Ng2YX1r2n@=87nY;?7hU*|pbLC(4b2$h{eABHZjISU7z}PM(L}n|R?_cL zy*2o>`WzfK!roi#r6FE>hG7gQvJxPN(_HIZZ8R3WY%pXY9@f2C`L7@yY^2{-?qY$2 zYK+J6?#Y~=#&Q;KTr4%sP1T4F#&!~DJo+gQR=~L(?MN65v|_+%|GymtBlyH0&CmtW#s#R%F3_H-tUFauOZv`qQ8S|E5`+hd6jXKsmQblvKl*PoTZrS zcI)8BM%LqieQjnI`9PduW8K)7J6!E_=ry=!n2P;$OTkSg#zp;5HseL!;?;hBiQ9qO z8ZT!o88vmP7Mk?X{OL|7OQ713L6Y{h)gpgW$1LOK zocDULWxu@(zwY3n1~?Yuo9AR$CffAxe3c2=HG!$S4D{gpn`WPnQx#dR049MZS{xs9 zSn6osiSe!d{kbR;H{8Nyx3zD(e%&EO2rwTpHV4#ATddO?qyd$?kh(NF4S*K<6o91t zby-#Irkz}7w%1Jc!U~UH4;8K;A2sC(6lefi^qNzOhqD2XjCREvaQZ|h#5!m4ygmWW zV~J6kg)x#yQh!~E86Q`>w_6iZ_(iJ{dKS?ZJJT7FPe~Q;2g7V@IeDCV4K7atRG^p7 z9yIBHT2ogxWCtxO1D?cNvgL52AXDE>KBz`WwBm+m3dM~vywtCo&cHQht|q!wC(42Y z-qL1v_EXi0jBkM=eYY^-E{x|hDqy(MIgLIk=kpK0+nB#>cNNC8SL@fOs)jFZvM@S*Yh5nf9z>yt!eH>7_53Qb^Cic0=972u%qXOqB{R92${w?(X z{2}t%K|>Aram6RCvI8u5+Vk|Q;YlG*<&xudABu?ECzAA2dXrV$f=mj{coD9A<-NLL zDfm$1ss68H66#Rfik8u9+;{%{1-pt}1Si2@hgC=k0I&1(oklT;h{STxsMFvFwW5!z z`RTP^%50#%5UV45cFUbj^{S5p$*3!U ze9EwDTwt?nE~p^O+%o{S5w6|H#$kQ{aU$`2KERN%1aUBXNEQ5qEA#0aKs*mL8B7C| zf7^%TLYKbEfM%Hq;=^mqH%gB|OJDL8y>Z+v6g(Bo(dMMIzlk~76Hx{VgZ~_T$dgGW ztD7o_SdA3uc>H*vGh#Xigqc}G4ZTT*xlhMz85Hc5`%=UIu|pMVvo!uY>&P+iXS5Cf zuhDM);WuptO*g_~E1;u<&n<|#eP9Rf!DY^z3JZaRWUOgV2=2>={XkR$trZ@bsmtBZ zl7uVp7B~VKi7a}-=l`|$-9b%m-QJ3zfJ%|3bVNl!nl$MsDk@b_K?ublpwdg|MY>Wg z6hQ(gU{oOV7J5hNQUU}*CqU>BdifrjM-RMbyx+{dbMMUYuNad3tiIO%t+jXV&x*@v z^E0KwhT}c$@el>{B>6JGMuSbyxMpJlg7_*>+0S}~RtM90>dvcC z+xL&H^A-T7NO&mM2RL;u1D(7Xv;Oahjh+8#wALRm#r}(Pn#an zKPf|bgs|Z%8uOxedVSHz7m_GF=tCuhhhN#;#+Wnxaq`Coao8}>7d;8jTyn5l z`Br6DGoN0rIr9FNC3LWVIj-UUapZ`u`w01qCzkIg;s%PUoM0sne|J@@ZR1h-PsY